<repo_name>Hl4p3x/tenkai-pow3<file_sep>/java/org/l2jmobius/gameserver/gui/AdminTab.java
package org.l2jmobius.gameserver.gui;

import org.l2jmobius.gameserver.gui.playertable.PlayerTablePane;
import org.l2jmobius.gameserver.model.announce.Announcement;
import org.l2jmobius.gameserver.util.Broadcast;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class AdminTab extends JPanel {
    private static final long serialVersionUID = 1L;
    private GridBagConstraints cons = new GridBagConstraints();
    private GridBagLayout layout = new GridBagLayout();
    private JPanel listPanel = new PlayerTablePane();
    private JPanel infoPanel = new JPanel();

    public AdminTab() {

        JTextArea talkadmin = new JTextArea();
        JButton bouton = new JButton("Send");
        bouton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Broadcast.toAllOnlinePlayers(talkadmin.getText());
            }
        });
        setLayout(layout);
        cons.fill = GridBagConstraints.HORIZONTAL;

        infoPanel.setLayout(layout);

        cons.insets = new Insets(5, 5, 5, 5);
        cons.gridwidth = 3;
        cons.gridheight = 20;
        cons.weightx = 1;
        cons.weighty = 1;
        cons.gridx = 0;
        cons.gridy = 2;

        infoPanel.add(bouton, cons);

        infoPanel.setPreferredSize(new Dimension(235, infoPanel.getHeight()));

        cons.fill = GridBagConstraints.BOTH;
        cons.weightx = 1;
        cons.weighty = 1;

        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listPanel, infoPanel);
        splitPane.setResizeWeight(0.3);
        splitPane.setDividerLocation(535);
        add(splitPane, cons);
        listPanel.add(talkadmin, cons);
    }
}<file_sep>/java/org/l2jmobius/gameserver/network/clientpackets/RequestRestart.java
/*
 * This file is part of the L2J Mobius project.
 * 
 * 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 org.l2jmobius.gameserver.network.clientpackets;

import java.util.logging.Logger;

import org.l2jmobius.commons.network.PacketReader;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.ConnectionState;
import org.l2jmobius.gameserver.network.Disconnection;
import org.l2jmobius.gameserver.network.GameClient;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.CharSelectionInfo;
import org.l2jmobius.gameserver.network.serverpackets.RestartResponse;
import org.l2jmobius.gameserver.util.OfflineTradeUtil;
import org.l2jmobius.gameserver.events.ElpiesArena.ElpiesArena;

/**
 * @version $Revision: 1.11.2.1.2.4 $ $Date: 2005/03/27 15:29:30 $
 */
public class RequestRestart implements IClientIncomingPacket
{
	protected static final Logger LOGGER_ACCOUNTING = Logger.getLogger("accounting");
	
	@Override
	public boolean read(GameClient client, PacketReader packet)
	{
		return true;
	}
	
	@Override
	public void run(GameClient client)
	{
		final PlayerInstance player = client.getPlayer();
		if (player == null)
		{
			return;
		}


		if (!player.canLogout())
		{
			client.sendPacket(RestartResponse.FALSE);
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}
		
		LOGGER_ACCOUNTING.info("Logged out, " + client);
		if (!OfflineTradeUtil.enteredOfflineMode(player))
		{
			Disconnection.of(client, player).storeMe().deleteMe();
		}
		if (ElpiesArena.elpy.containsKey(player.getObjectId())) {
			ElpiesArena.getInstance().removePlayer(player);
		}

		// return the client to the authed status
		client.setConnectionState(ConnectionState.AUTHENTICATED);
		
		client.sendPacket(RestartResponse.TRUE);
		
		// send char list
		final CharSelectionInfo cl = new CharSelectionInfo(client.getAccountName(), client.getSessionId().playOkID1);
		client.sendPacket(cl);
		client.setCharSelection(cl.getCharInfo());
	}
}
<file_sep>/data/scripts/ai/bosses/Piggy/Piggy.java
/*
 * This file is part of the L2J Mobius project.
 *
 * 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 ai.bosses.Piggy;

import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.ai.CtrlIntention;
import org.l2jmobius.gameserver.data.xml.impl.SkillData;
import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.enums.MountType;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Attackable;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Playable;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.holders.SkillHolder;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.network.NpcStringId;
import org.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
import org.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.NpcSay;
import org.l2jmobius.gameserver.util.Util;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Future;

import ai.AbstractNpcAI;

/**
 * Piggy' AI.
 *
 * @author Barroso_K
 */
public class Piggy extends AbstractNpcAI {
    // NPC
    private static final int PIGGY = 9009120;

    private static final String[] PIGGY_SPEACH_ON_HIT = {
            "Stop hitting me please",
            "What have i done to deserve that ?",
            "Why are you so mean to me ?",
            "Stop it hurts",
            "I have kids !",
            "Stop i'll give you some adena !",
            "I won't forgive you"
    };
    private long _lastSpoke = 0;
    private int _speakInterval = 10; // Seconds

    private int _angryTime = 5; // Seconds
    public static Future<?> _timer;
    public int _timeLeft = 0;

    private final PiggySkill[] PIGGY_SKILLS =
            {
                    new PiggySkill(15271, 20, 100, true, 1000), // Body Slam
                    new PiggySkill(15287, 25, 250, false), // Twister
                    new PiggySkill(15305, 30, 300, false, 400), // Hurricane
                    new PiggySkill(15342, 10, 500, false) // Shock Smash
            };

    private class PiggySkill {
        private int skillId;
        private int brutDamages;
        private int percentDamage;
        private boolean isAoe;
        private int range;

        PiggySkill(int skillId, int percentDamage, int brutDamages, boolean isAoe, int range) {
            this.skillId = skillId;
            this.percentDamage = percentDamage;
            this.brutDamages = brutDamages;
            this.isAoe = isAoe;
            this.range = range;
        }

        PiggySkill(int skillId, int percentDamage, int brutDamages, boolean isAoe) {
            this.skillId = skillId;
            this.percentDamage = percentDamage;
            this.brutDamages = brutDamages;
            this.isAoe = isAoe;
        }

        public int getSkillId() {
            return this.skillId;
        }

        public int getPercentDamage() {
            return this.percentDamage;
        }

        public boolean isAoe() {
            return this.isAoe;
        }

        public int getRange() {
            return this.range;
        }

        public Skill getSkill() {
            return SkillData.getInstance().getSkill(this.skillId, 1);
        }

        public int getBrutDamages() {
            return this.brutDamages;
        }
    }

    private Playable _actualVictim;
    private boolean _isAngry = false;
    private static Npc _piggy;
    public static Location PIGGY_LOC = new Location(-51640, -70248, -3418);
    public static boolean isSpawned = false;

    public static int[] servitors = {9009121, 9009122, 9009123};
    public static Vector<Npc> _servitors = new Vector<>();
    private static final SkillHolder SERVITOR_HEAL = new SkillHolder(1401, 10);
    private static final String[] SERVITORS_SPEACH = {
            "Here's some heal master !!",
            "Are you okay king ?",
            "I'm coming for you master !",
            "I'm here to help you !",
            "I'll take care of you don't worry master",
            "The doctor is here !",
            "Let me take care of this my king",
            "You will soon feel better believe me"
    };

    public static int SCARECROW = 9009124;
    public static Location SCARECROW_LOC = new Location(-51624, -68840, -3418);
    public static Npc _scarecrow;
    public static boolean _scarecrowSpawned = false;
    public int _totalhits = 0;
    public int _maxHits = 100;

    private static final int NPC_ID = 9009126;
    private Npc _teleporter;

    public Piggy() {
        registerMobs(PIGGY);
        addAttackId(SCARECROW);
        for (int mob : servitors) {
            addAttackId(mob);
            addKillId(mob);
        }
        if (isSpawned) {
            return;
        }

        addStartNpc(NPC_ID);
        addTalkId(NPC_ID);
        addFirstTalkId(NPC_ID);

        _teleporter = addSpawn(NPC_ID, 147414, 27928, -2271, 20352, false, 0);
        _teleporter.setTitle("Piggy Raidboss");


        if (_piggy != null) {
            _piggy.deleteMe();
        }
        _piggy = addSpawn(PIGGY, PIGGY_LOC, false, 0);
        isSpawned = true;
        startQuestTimer("regen_task", 10000, _piggy, null, true);
        startQuestTimer("think", 10000, _piggy, null, true);
        startQuestTimer("skill_task", 3000, _piggy, null, true);
        startQuestTimer("spawn_servitors", 100, _piggy, null, false);
        startQuestTimer("heal_the_king", 10000, _piggy, null, true);
    }

    @Override
    public String onAdvEvent(String event, Npc npc, PlayerInstance player) {
        if (npc != null) {
            if (event.equalsIgnoreCase("skill_task")) {
                callSkillAI(npc);
            } else if (event.equalsIgnoreCase("spawn_scarecrow")) {
                if (_scarecrow != null) {
                    return "";
                }
                _scarecrow = addSpawn(SCARECROW, SCARECROW_LOC.getX(), SCARECROW_LOC.getY(), SCARECROW_LOC.getZ(), -1, false, 0, true, 0);
                _scarecrowSpawned = true;
                startQuestTimer("unspawn_scarecrow", 10000, _piggy, null, false);
            } else if (event.equalsIgnoreCase("think")) {
                if (getRandom(20) == 0 && !_isAngry && !_scarecrowSpawned) {
                    startQuestTimer("spawn_scarecrow", 1, _piggy, null, false);
                }
            } else if (event.equalsIgnoreCase("unspawn_scarecrow")) {
                _scarecrow.deleteMe();
                _scarecrowSpawned = false;
                if (_totalhits > _maxHits) {
                    _totalhits = _maxHits;
                }
                _piggy.reduceCurrentHp(getDamages(_piggy.getMaxHp(), (int) ((float) _totalhits / _maxHits * 100), 0), null, null, false, true, false, false);
                _totalhits = 0;
            } else if (event.equalsIgnoreCase("spawn_servitors")) {
                if (_servitors.size() >= 15) {
                    return "";
                }
                int radius = 250;
                for (int a = 0; a < 2; a++) {
                    for (int i = 0; i < 7; i++) {
                        int x = (int) (radius * Math.cos(i * 0.618));
                        int y = (int) (radius * Math.sin(i * 0.618));
                        Npc pig = addSpawn(servitors[Rnd.get(servitors.length)], PIGGY_LOC.getX() + x, PIGGY_LOC.getY() + y, PIGGY_LOC.getZ() + 20, -1, false, 0, true, 0);
                        _servitors.add(pig);
                    }
                    radius += 300;
                }
                startQuestTimer("spawn_servitors", 300000, _piggy, null, true);
            } else if (event.equalsIgnoreCase("heal_the_king")) {
                for (Npc pig : _servitors) {
                    if (getRandom(20) == 0) {
                        if (Util.checkIfInRange((SERVITOR_HEAL.getSkill().getCastRange() < 600) ? 600 : SERVITOR_HEAL.getSkill().getCastRange(), pig, _piggy, true)) {
                            pig.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                            pig.setTarget(_piggy);
                            pig.doCast(SERVITOR_HEAL.getSkill());
                            pig.broadcastPacket(new NpcSay(pig.getObjectId(),
                                    ChatType.NPC_GENERAL,
                                    pig.getTemplate().getId(), getRandomEntry(SERVITORS_SPEACH)));
                        } else {
                            pig.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _piggy, null);
                        }
                    }
                }
            } else if (event.equalsIgnoreCase("get_angry")) {
                _isAngry = true;
                _timeLeft = _angryTime;
                _timer = ThreadPool.scheduleAtFixedRate(new Runnable() {
                    @Override
                    public void run() {
                        World.getInstance().forEachVisibleObjectInRange(_piggy, Playable.class, 1200, c ->
                        {
                            // c.sendPacket(new ExSendUIEvent(c.getActingPlayer(), ExSendUIEvent.TYPE_NORNIL, _timeLeft--, 0, 0, 0, 0, 2518008));
                            c.sendPacket(new ExSendUIEvent(c.getActingPlayer(), ExSendUIEvent.TYPE_NORNIL, _timeLeft--, _angryTime, NpcStringId.YOU_MADE_ME_ANGRY));
                        });

                    }
                }, 0, 1000);

                startQuestTimer("stop_angry", _angryTime * 1000, _piggy, null, false);
            } else if (event.equalsIgnoreCase("stop_angry")) {
                if (!_isAngry) {
                    return "";
                }
                _timer.cancel(true);
                _isAngry = false;
                _piggy.broadcastPacket(new NpcSay(_piggy.getObjectId(),
                        ChatType.NPC_GENERAL,
                        _piggy.getTemplate().getId(), "Thanks i'm a bit more relaxed now"));

            }
        }
        return super.onAdvEvent(event, npc, player);
    }

    @Override
    public String onSpawn(Npc npc) {
        ((Attackable) npc).setCanReturnToSpawnPoint(false);
        npc.setRandomWalking(true);
        npc.disableCoreAI(true);
        return super.onSpawn(npc);
    }

    @Override
    public String onAttack(Npc npc, PlayerInstance attacker, int damage, boolean isSummon) {
        if (npc.getId() == PIGGY) {

            if (npc.isHpBlocked()) {
                return null;
            }

            if (_lastSpoke + (_speakInterval * 1000) < System.currentTimeMillis()) {
                _piggy.broadcastPacket(new NpcSay(_piggy.getObjectId(),
                        ChatType.NPC_GENERAL,
                        _piggy.getTemplate().getId(), getRandomEntry(PIGGY_SPEACH_ON_HIT)));
                _lastSpoke = System.currentTimeMillis();
            }

            if (_isAngry) {
                _piggy.broadcastPacket(new NpcSay(_piggy.getObjectId(),
                        ChatType.NPC_GENERAL,
                        _piggy.getTemplate().getId(), "I TOLD YOU I WAS ANGRY !"));
                _piggy.doCast(PIGGY_SKILLS[0].getSkill());
                ThreadPool.schedule(() -> {
                    World.getInstance().forEachVisibleObjectInRange(_piggy, Playable.class, 1200, c ->
                    {
                        final int hpRatio = (int) ((c.getCurrentHp() / c.getMaxHp()) * 100);
                        if (hpRatio > 10) {
                            c.reduceCurrentHp(getDamages(c.getMaxHp(), PIGGY_SKILLS[0].getPercentDamage() * 2, PIGGY_SKILLS[0].getBrutDamages()), _piggy, PIGGY_SKILLS[0].getSkill(), false, true, false, false);
                        }
                    });
                    _timer.cancel(true);
                    _isAngry = false;
                }, PIGGY_SKILLS[0].getSkill().getHitTime());
                return "";
            }

            // Debuff strider-mounted players.
            if ((attacker.getMountType() == MountType.STRIDER) && !attacker.isAffectedBySkill(4258)) {
                npc.setTarget(attacker);
                npc.doCast(SkillData.getInstance().getSkill(4258, 1));
            }
        } else if (npc.getId() == SCARECROW) {
            _totalhits++;
            World.getInstance().forEachVisibleObjectInRange(_piggy, Playable.class, 1200, c ->
            {
                c.sendPacket(new ExSendUIEvent(c.getActingPlayer(), ExSendUIEvent.TYPE_NORNIL, _totalhits, _maxHits, NpcStringId.HIT_ME_MORE_HIT_ME_MORE));
            });

        } else {

        }
        return super.onAttack(npc, attacker, damage, isSummon);
    }

    @Override
    public String onKill(Npc npc, PlayerInstance killer, boolean isSummon) {

        if (npc.getId() == PIGGY) {
            cancelQuestTimer("regen_task", npc, null);
            cancelQuestTimer("think", npc, null);
            cancelQuestTimer("skill_task", npc, null);
            cancelQuestTimer("spawn_servitors", npc, null);
            cancelQuestTimer("heal_king", npc, null);
            isSpawned = false;
            for (Npc s : _servitors) {
                s.deleteMe();
            }
        }
        return super.onKill(npc, killer, isSummon);
    }

    @Override
    public String onAggroRangeEnter(Npc npc, PlayerInstance player, boolean isSummon) {
        return null;
    }

    private void callSkillAI(Npc npc) {
        if (npc.isInvul() || npc.isCastingNow()) {
            return;
        }

        if (!_isAngry && getRandom(20) == 0 && !_scarecrowSpawned) {
            _piggy.broadcastPacket(new NpcSay(npc.getObjectId(),
                    ChatType.NPC_GENERAL,
                    _piggy.getTemplate().getId(), "If you touch me i'll get angry guys"));

            World.getInstance().forEachVisibleObjectInRange(_piggy, Playable.class, 1200, c ->
            {
                c.sendPacket(new ExShowScreenMessage("Piggy will be angry very soon, care !", 5000));
            });
            startQuestTimer("get_angry", 3000, _piggy, null, false);
        }

        // Pickup a target if no or dead victim. 10% luck he decides to reconsiders his target.
        if ((_actualVictim == null) || _actualVictim.isDead() || !(npc.isInSurroundingRegion(_actualVictim)) || (getRandom(10) == 0)) {
            _actualVictim = getRandomTarget(npc);
        }

        if (_actualVictim == null) {
            if (getRandom(10) == 0) {
                final int x = npc.getX();
                final int y = npc.getY();
                final int z = npc.getZ();

                final int posX = x + getRandom(-1400, 1400);
                final int posY = y + getRandom(-1400, 1400);

                if (GeoEngine.getInstance().canMoveToTarget(x, y, z, posX, posY, z, npc.getInstanceWorld())) {
                    npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(posX, posY, z, 0));
                }
            }
            return;
        }

        final PiggySkill skill = getRandomSkill(npc);

        // Cast the skill or follow the target.
        if (Util.checkIfInRange((skill.getSkill().getCastRange() < 600) ? 600 : skill.getSkill().getCastRange(), npc, _actualVictim, true)) {
            npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
            npc.setTarget(_actualVictim);
            System.out.println("Skill: " + skill.getSkill().getName());
            npc.doCast(skill.getSkill());
            ThreadPool.schedule(() -> {
                if (skill.isAoe()) {
                    World.getInstance().forEachVisibleObjectInRange(_piggy, Playable.class, skill.getRange(), c ->
                    {
                        final int hpRatio = (int) ((c.getCurrentHp() / c.getMaxHp()) * 100);
                        if (hpRatio > 10) {
                            c.reduceCurrentHp(getDamages(c.getMaxHp(), skill.getPercentDamage(), skill.getBrutDamages()), _piggy, skill.getSkill(), false, true, false, false);
                        }
                    });

                } else {
                    _actualVictim.reduceCurrentHp(getDamages(_actualVictim.getMaxHp(), skill.getPercentDamage(), skill.getBrutDamages()), _piggy, skill.getSkill(), false, true, false, false);
                }
            }, skill.getSkill().getHitTime());
        } else {
            npc.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _actualVictim, null);
        }
    }

    private float getDamages(int totalHp, int percent, int brut) {
        return (totalHp - totalHp * (((100 - percent) / 100f))) + brut;
    }

    /**
     * Pick a random skill.<br>
     * Piggy will mostly use utility skills. If Piggy feels surrounded, he will use AoE skills.<br>
     * Lower than 50% HPs, he will begin to use Meteor skill.
     *
     * @param npc piggy
     * @return a skill holder
     */
    private PiggySkill getRandomSkill(Npc npc) {
        final int hpRatio = (int) ((npc.getCurrentHp() / npc.getMaxHp()) * 100);

        return getRandomEntry(PIGGY_SKILLS);
    }

    /**
     * Pickup a random Playable from the zone, deads targets aren't included.
     *
     * @param npc
     * @return a random Playable.
     */
    private Playable getRandomTarget(Npc npc) {
        final List<Playable> result = new ArrayList<>();

        World.getInstance().forEachVisibleObject(npc, Playable.class, obj ->
        {
            if ((obj == null) || obj.isPet()) {
                return;
            } else if (!obj.isDead() && obj.isPlayable()) {
                result.add(obj);
            }
        });

        return getRandomEntry(result);
    }

    @Override
    public String onFirstTalk(Npc npc, PlayerInstance player) {
        StringBuilder tb = new StringBuilder();
        tb.append("<html><center><font color=\"3D81A8\">Piggy Raidboss</font></center><br1>Hi " + player.getName() + "<br>");
        tb.append("<font color=\"3D81A8\">Available Actons:</font><br>");
        tb.append("<br>");
        tb.append("<center><button value=\"Teleport to Fantasy Island\" action=\"bypass -h gm_event piggy_raidboss teleport_to_fantasy\"\n" +
                "                            width=200\n" +
                "                            height=40 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_CT1.Button_DF\">\n" +
                "              </center> ");
        tb.append("</body></html>");
        NpcHtmlMessage msg = new NpcHtmlMessage(NPC_ID);
        msg.setHtml(tb.toString());
        player.sendPacket(msg);
        return "";
    }


}<file_sep>/config/Custom/NpcStatMultipliers.ini
# ---------------------------------------------------------------------------
# NPC Stat Multipliers
# ---------------------------------------------------------------------------

# Enable/Disable NPC stat multipliers.
EnableNpcStatMultipliers = True

# Monsters
MonsterHP = 0.5
MonsterMP = 0.5
MonsterPAtk = 1.0
MonsterMAtk = 1.0
MonsterPDef = 0.4
MonsterMDef = 0.4
MonsterAggroRange = 1.0
MonsterClanHelpRange = 1.0

# Raidbosses
RaidbossHP = 1.0
RaidbossMP = 1.0
RaidbossPAtk = 1.0
RaidbossMAtk = 1.0
RaidbossPDef = 0.5
RaidbossMDef = 0.5
RaidbossAggroRange = 1.0
RaidbossClanHelpRange = 1.0

# Guards
GuardHP = 2.0
GuardMP = 2.0
GuardPAtk = 2.0
GuardMAtk = 2.0
GuardPDef = 2.0
GuardMDef = 2.0
GuardAggroRange = 1.0
GuardClanHelpRange = 1.0

# Defenders
DefenderHP = 2.0
DefenderMP = 2.0
DefenderPAtk = 2.0
DefenderMAtk = 2.0
DefenderPDef = 2.0
DefenderMDef = 2.0
DefenderAggroRange = 1.0
DefenderClanHelpRange = 1.0
<file_sep>/config/TrainingCamp.ini
# ---------------------------------------------------------------------------
# Training Camp
# ---------------------------------------------------------------------------

# Enable or disable Training Camp
# Default: True
TrainingCampEnable = True

# Only Premium account can access training camp
# Default: True
TrainingCampPremiumOnly = True

# Max duration for Training Camp in seconds. NA : 18000, RU : 36000
# Default: 18000
TrainingCampDuration = 18000

# Min level to enter Training Camp
# Default: 18
TrainingCampMinLevel = 18

# Max level to enter Training Camp
# Default: 127
TrainingCampMaxLevel = 127

# Multiplier for rewarded EXP
# Default: 1.0
TrainingCampExpMultiplier = 2.0

# Multiplier for rewarded SP
# Default: 1.0
TrainingCampSpMultiplier = 2.0
<file_sep>/config/Custom/AutoPotions.ini
# ---------------------------------------------------------------------------
# Auto Potion Settings
# ---------------------------------------------------------------------------
# Use .apon / .apoff voiced commands to enable / disable.

# Enable auto potion commands.
AutoPotionsEnabled = True

# Use auto potions in Olympiad.
AutoPotionsInOlympiad = false

# Minimum player level to use the commands.
AutoPotionMinimumLevel = 1

# Enable auto CP potions.
AutoCpEnabled = true
# Percentage that CP potions will be used.
AutoCpPercentage = 70
# Auto CP item ids. Order by use priority.
AutoCpItemIds = 5592,5591

# Enable auto HP potions.
AutoHpEnabled = true
# Percentage that HP potions will be used.
AutoHpPercentage = 70
# Auto HP item ids. Order by use priority.
AutoHpItemIds = 1540,1539,1061,1060

# Enable auto MP potions.
AutoMpEnabled = true
# Percentage that MP potions will be used.
AutoMpPercentage = 70
# Auto MP item ids. Order by use priority.
AutoMpItemIds = 728
<file_sep>/data/scripts/handlers/voicedcommandhandlers/MyHiddenStats.java
/*
 * This file is part of the L2J Mobius project.
 * 
 * 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 scripts.handlers.voicedcommandhandlers;

import org.l2jmobius.Config;
import org.l2jmobius.gameserver.handler.IVoicedCommandHandler;
import org.l2jmobius.gameserver.instancemanager.PremiumManager;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.stats.Stat;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;

public class MyHiddenStats implements IVoicedCommandHandler
{
	private static final String[] VOICED_COMMANDS =
	{
		"myhiddenstats"
	};
	
	@Override
	public boolean useVoicedCommand(String command, PlayerInstance player, String target)
	{
		if (command.startsWith("myhiddenstats"))
		{
			final NpcHtmlMessage msg = new NpcHtmlMessage(5);

			String html = "<html>" + "<body>" +
					"<center><table><tr><td><img src=icon.etc_alphabet_h_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_i_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_d_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_d_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_e_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_n_i00 width=32 height=32></td></tr></table></center>" +
					"<center><table><tr><td><img src=icon.etc_alphabet_s_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_t_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_a_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_t_i00 width=32 height=32></td><td><img src=icon.etc_alphabet_s_i00 width=32 height=32></td></tr></table></center><br><br>" +
					"<center><table width=300><tr><td FIXWIDTH=150>Stat</td><td FIXWIDTH=150>Value</td</tr></table><img src=\"L2UI.Squaregray\" width=300 height=1>";

			html += getStatHtm("Shield Def", player.getShldDef(), 0, false, "");
			html += getStatHtm("Dmg Absorbed", player.getStat().getValue(Stat.ABSORB_DAMAGE_PERCENT, 0), 0, false, "%");
			html += getStatHtm("Heal Effect", player.getStat().getValue(Stat.HEAL_EFFECT, 100) - 100, 0, true,
					"%");
			html += getStatHtm("Heals Effect Add", player.getStat().getValue(Stat.HEAL_EFFECT_ADD, 100) - 100, 0, true, "%");
			html += getStatHtm("Momentum Max", player.getStat().getValue(Stat.MAX_MOMENTUM, 100) - 100, 0, true,
					"%");
			html += getStatHtm("Max Targets", player.getStat().getValue(Stat.ATTACK_COUNT_MAX, 1) - 1, 0, true, "");
			html += getStatHtm("P. Skill Power", player.getStat().getValue(Stat.PHYSICAL_SKILL_POWER, 100) - 100, 0,
					true, "%");
			html += getStatHtm("P. Crit Dmg", player.getStat().getValue(Stat.CRITICAL_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("P. Crit Dmg Recvd", player.getStat().getValue(Stat.DEFENCE_CRITICAL_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("Skill Crit", player.getStat().getValue(Stat.SKILL_CRITICAL, 100) - 100, 0, true,
					"%");
			html += getStatHtm("Skill Crit Probability", player.getStat().getValue(Stat.SKILL_CRITICAL_PROBABILITY, 100) - 100, 0, true,
					"%");
			html += getStatHtm("M. Skill Power", player.getStat().getValue(Stat.MAGICAL_SKILL_POWER, 100) - 100, 0, true,
					"%");
			html += getStatHtm("M. Crit Dmg", player.getStat().getValue(Stat.MAGIC_CRITICAL_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("M. Crit Dmg Recvd", player.getStat().getValue(Stat.DEFENCE_MAGIC_CRITICAL_DAMAGE, 100) - 100, 0, true,
					"%");
			html += getStatHtm("Fixed P. Crit Dmg", player.getStat().getValue(Stat.CRITICAL_DAMAGE_ADD, 0), 0, true, "");
			html += getStatHtm("Reflected Dmg", player.getStat().getValue(Stat.REFLECT_DAMAGE_PERCENT, 0), 0, true, "%");
			html += getStatHtm("Reflection Resistance", player.getStat().getValue(Stat.REFLECT_DAMAGE_PERCENT_DEFENSE, 0), 0, true, "%");

			html += getStatHtm("PvP P. Dmg", player.getStat().getValue(Stat.PVP_PHYSICAL_ATTACK_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("PvP P. Skill Dmg", player.getStat().getValue(Stat.PVP_PHYSICAL_SKILL_DAMAGE, 100) - 100, 0,
					true, "%");
			html += getStatHtm("PvP M. Dmg", player.getStat().getValue(Stat.PVP_MAGICAL_SKILL_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("PvP P. Dmg Res", player.getStat().getValue(Stat.PVP_PHYSICAL_ATTACK_DEFENCE, 100) - 100, 0, true,
					"%");
			html += getStatHtm("PvP P. Skill Dmg Res", player.getStat().getValue(Stat.PVP_PHYSICAL_SKILL_DEFENCE, 100) - 100, 0,
					true, "%");
			html += getStatHtm("PvP M. Dmg Res", player.getStat().getValue(Stat.PVP_MAGICAL_SKILL_DEFENCE, 100) - 100, 0, true,
					"%");

			html += getStatHtm("PvE P. Dmg", player.getStat().getValue(Stat.PVE_PHYSICAL_ATTACK_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("PvE P. Skill Dmg", player.getStat().getValue(Stat.PVE_PHYSICAL_SKILL_DAMAGE, 100) - 100, 0,
					true, "%");
			html += getStatHtm("PvE M. Dmg", player.getStat().getValue(Stat.PVE_MAGICAL_SKILL_DAMAGE, 100) - 100, 0, true, "%");
			html += getStatHtm("PvE P. Dmg Res", player.getStat().getValue(Stat.PVE_PHYSICAL_ATTACK_DEFENCE, 100) - 100, 0, true,
					"%");
			html += getStatHtm("PvE P. Skill Dmg Res", player.getStat().getValue(Stat.PVE_PHYSICAL_SKILL_DEFENCE, 100) - 100, 0,
					true, "%");
			html += getStatHtm("PvE M. Dmg Res", player.getStat().getValue(Stat.PVE_MAGICAL_SKILL_DEFENCE, 100) - 100, 0, true,
					"%");
			html += getStatHtm("Resist abnormal debuff", player.getStat().getValue(Stat.RESIST_ABNORMAL_DEBUFF, 100) - 100, 0, true, "%");
			html += getStatHtm("Physical abnormal Res", player.getStat().getValue(Stat.ABNORMAL_RESIST_PHYSICAL, 100) - 100, 0,
					true, "%");
			html += getStatHtm("Magical abnormal Res", player.getStat().getValue(Stat.ABNORMAL_RESIST_MAGICAL, 100) - 100, 0,
					true, "%");
			html += getStatHtm("Immobile Res", player.getStat().getValue(Stat.IMMOBILE_DAMAGE_RESIST, 100) - 100, 0, true, "%");
			html += getStatHtm("Dispell Res", player.getStat().getValue(Stat.RESIST_DISPEL_BUFF, 100) - 100, 0, true, "%");
			html += getStatHtm("absorbDamChance", player.getStat().getValue(Stat.ABSORB_DAMAGE_CHANCE, 100) - 100, 0, true, "%");
			html += getStatHtm("FixedDamageResist", player.getStat().getValue(Stat.REAL_DAMAGE_RESIST, 100) - 100, 0, true, "%");




			html += "</table><br>" + "</body></html>";
			player.sendPacket(new NpcHtmlMessage(0, html));
			
			msg.setHtml(html);
			player.sendPacket(msg);
		}
		else
		{
			return false;
		}
		return true;
	}

	private String getStatHtm(String statName, double statVal, double statDefault, boolean plusIfPositive, String suffix)
	{
		if (statVal == statDefault)
		{
			return "";
		}

		return "<table width=300 border=0><tr><td FIXWIDTH=150>" + statName + ":</td><td FIXWIDTH=150>" +
				(plusIfPositive && statVal >= 0 ? "+" : "") + new DecimalFormat("#0.##").format(statVal) + suffix +
				"</td></tr></table><img src=\"L2UI.Squaregray\" width=300 height=1>";
	}


	@Override
	public String[] getVoicedCommandList()
	{
		return VOICED_COMMANDS;
	}
}<file_sep>/java/org/l2jmobius/gameserver/util/Point3D.kt
package org.l2jmobius.gameserver.util

import java.io.Serializable

class Point3D : Serializable {

    @Volatile
    @set:Synchronized
    var x: Int = 0
    @Volatile
    @set:Synchronized
    var y: Int = 0
    @Volatile
    @set:Synchronized
    var z: Int = 0

    constructor(pX: Int, pY: Int, pZ: Int) {
        x = pX
        y = pY
        z = pZ
    }

    constructor(pX: Int, pY: Int) {
        x = pX
        y = pY
        z = 0
    }

    constructor(worldPosition: Point3D) {
        synchronized(worldPosition) {
            x = worldPosition.x
            y = worldPosition.y
            z = worldPosition.z
        }
    }

    @Synchronized
    fun setTo(point: Point3D) {
        synchronized(point) {
            x = point.x
            y = point.y
            z = point.z
        }
    }

    override fun toString(): String {
        return "($x, $y, $z)"
    }

    override fun hashCode(): Int {
        return x xor y xor z
    }

    @Synchronized
    override fun equals(other: Any?): Boolean {
        if (other is Point3D) {
            val point3D = other
            var ret = false
            synchronized(point3D) {
                ret = point3D.x == x && point3D.y == y && point3D.z == z
            }
            return ret
        }
        return false
    }

    @Synchronized
    fun equals(pX: Int, pY: Int, pZ: Int): Boolean {
        return x == pX && y == pY && z == pZ
    }

    @Synchronized
    fun distanceSquaredTo(point: Point3D): Long {
        var dx = 0L
        var dy = 0L
        synchronized(point) {
            dx = (x - point.x).toLong()
            dy = (y - point.y).toLong()
        }
        return dx * dx + dy * dy
    }

    @Synchronized
    fun setXYZ(pX: Int, pY: Int, pZ: Int) {
        x = pX
        y = pY
        z = pZ
    }

    companion object {
        private const val serialVersionUID = 4638345252031872576L
    }
}<file_sep>/config/Event.ini
# ---------------------------------------------------------------------------
#       Enable/Disable InstancedEvent System
# ---------------------------------------------------------------------------
InstancedEventEnabled = true

# ---------------------------------------------------------------------------
#       Time Between events (in minutes, 300 = 5 hours)
# ---------------------------------------------------------------------------
InstancedEventInterval = 60

# ---------------------------------------------------------------------------
#       Event running time (in minutes).
# ---------------------------------------------------------------------------
  InstancedEventRunningTime = 10

# ---------------------------------------------------------------------------
#       Min/Max amount of players allowed in each team.
# ---------------------------------------------------------------------------
InstancedEventMinPlayersInTeams = 2

# ---------------------------------------------------------------------------
#       Respawn and exit delay timers (in seconds);
# ---------------------------------------------------------------------------
InstancedEventRespawnTeleportDelay = 5
InstancedEventStartLeaveTeleportDelay = 5

InstancedEventTargetTeamMembersAllowed = true
InstancedEventScrollsAllowed = false
InstancedEventPotionsAllowed = false
InstancedEventSummonByItemAllowed = false
InstancedEventRewardTeamTie = true
InstancedEventMinPlayerLevel = 85

# ---------------------------------------------------------------------------
#        Participant's effects handling on teleport/death.
#       Effects lasting through death never removed.
#        0 - always remove all effects.
#        1 - remove all effects only during port to event (noblesse blessing can be used)
#        2 - never remove any effect
# ---------------------------------------------------------------------------
InstancedEventEffectsRemoval = 0
<file_sep>/java/org/l2jmobius/gameserver/events/CreatureInvasion/CreatureInvasion.java
package org.l2jmobius.gameserver.events.CreatureInvasion;

import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.quest.Quest;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.network.serverpackets.NpcSay;
import org.l2jmobius.gameserver.util.Broadcast;

import java.util.*;

public class CreatureInvasion extends Quest {
    //Config
    private static final int timeToEndInvasion = 15; //Minutes
    private static final int[] invaderIds = {900900, 900901};
    private static final int rewardId = 10639;
    private static final int[][] rewards = {{5, 10}, {10, 20}};
    private Map<Integer, int[]> invadersReward = new HashMap<Integer, int[]>();
    //Vars
    private static boolean isUnderInvasion = false;
    private Map<Integer, invaderInfo> attackInfo = new HashMap<Integer, invaderInfo>();
    private ArrayList<Creature> invaders = new ArrayList<Creature>();

    private static final int NPC_ID = 9009125;
    private Npc _teleporter;

    public void StartInvasion() {
        if (isUnderInvasion) {
            startQuestTimer("end_invasion", 1, null, null, false);
        } else {
            startQuestTimer("start_invasion", 1, null, null, false);
        }
    }

    public CreatureInvasion(int id) {
        super(id);
        int i = 0;
        for (int mob : invaderIds) {
            invadersReward.put(mob, rewards[i]);
            addAttackId(mob);
            addKillId(mob);
            i++;
        }
    }


    private class invaderInfo {
        private Long attackedTime;
        private int playerId;
        private String externalIP;

        private invaderInfo(int playerId, String externalIP) {
            this.playerId = playerId;
            this.externalIP = externalIP;
            setAttackedTime();
        }

        private long getAttackedTime() {
            return attackedTime;
        }

        private void setAttackedTime() {
            attackedTime = System.currentTimeMillis();
        }

        private int getPlayerId() {
            return playerId;
        }

        private String getExternalIP() {
            return externalIP;
        }

        private void updateInfo(int playerId, String externalIP) {
            this.playerId = playerId;
            this.externalIP = externalIP;
            setAttackedTime();
        }
    }

    @Override
    public String onAttack(Npc npc, PlayerInstance player, int damage, boolean isPet, Skill skill) {
        if (!isUnderInvasion) {
            player.doDie(npc);
            return "";
        }

        synchronized (attackInfo) {
            invaderInfo info = attackInfo.get(npc.getObjectId()); //Get the attack info from this npc

            int sameIPs = 0;
            int underAttack = 0;

            for (Map.Entry<Integer, invaderInfo> entry : attackInfo.entrySet()) {
                if (entry == null) {
                    continue;
                }

                invaderInfo i = entry.getValue();
                if (i == null) {
                    continue;
                }

                if (System.currentTimeMillis() < i.getAttackedTime() + 5000) {
                    if (i.getPlayerId() == player.getObjectId()) {
                        underAttack++;
                    }
                    if (i.getExternalIP().equalsIgnoreCase(player.getIPAddress())) {
                        sameIPs++;
                    }
                    if (underAttack > 1 || sameIPs > 1) {
                        player.doDie(npc);
                        if (underAttack > 1) {
                            npc.broadcastPacket(new NpcSay(npc.getObjectId(),
                                    ChatType.NPC_GENERAL,
                                    npc.getTemplate().getId(),
                                    player.getName() + " you cant attack more than one mob at same time!"));
                        } else if (sameIPs > 1) {
                            npc.broadcastPacket(new NpcSay(npc.getObjectId(),
                                    ChatType.NPC_GENERAL,
                                    npc.getTemplate().getId(),
                                    player.getName() + " dualbox is not allowed here!"));
                        }
                        return "";
                    }
                }
            }

            if (info == null) //Don't exist any info from this npc
            {
                //Add the correct info
                info = new invaderInfo(player.getObjectId(), player.getIPAddress());
                //Insert to the map
                attackInfo.put(npc.getObjectId(), info);
            } else {
                //Already exists information for this NPC
                //Check if the attacker is the same as the stored
                if (info.getPlayerId() != player.getObjectId()) {
                    //The attacker is not same
                    //If the last attacked stored info +10 seconds is bigger than the current time, this mob is currently attacked by someone
                    if (info.getAttackedTime() + 5000 > System.currentTimeMillis()) {
                        player.doDie(npc);
                        npc.broadcastPacket(new NpcSay(npc.getObjectId(),
                                ChatType.NPC_GENERAL,
                                npc.getTemplate().getId(),
                                player.getName() + " don't attack mobs from other players!"));
                        return "";
                    } else {
                        //Add new information, none is currently attacking this NPC
                        info.updateInfo(player.getObjectId(), player.getIPAddress());
                    }
                } else {
                    //player id is the same, update the attack time
                    info.setAttackedTime();
                }
            }
        }
        return super.onAttack(npc, player, damage, isPet, skill);
    }

    @Override
    public String onKill(Npc npc, PlayerInstance player, boolean isPet) {
        synchronized (attackInfo) {
            invaderInfo info = attackInfo.get(npc.getObjectId()); //Get the attack info
            if (info != null) {
                attackInfo.remove(npc.getObjectId()); //Delete the stored info for this npc
            }
        }

        if (isUnderInvasion) {
            Npc inv =
                    addSpawn(invaderIds[Rnd.get(invaderIds.length)], npc.getX() + Rnd.get(100), npc.getY() + Rnd.get(100), npc.getZ(), 0, false, 0);
            invaders.add(inv);
        }
        int[] rewards = invadersReward.get(npc.getId());
        player.addItem("event", rewardId, randomBetween(rewards[0], rewards[1]), player, true);
        return super.onKill(npc, player, isPet);
    }

    public int randomBetween(int low, int high) {
        Random r = new Random();
        return r.nextInt(high - low) + low;
    }

    @SuppressWarnings("unused")
    @Override
    public String onAdvEvent(String event, Npc npc, PlayerInstance player) {
        if (event.equalsIgnoreCase("teleport_to_fantasy")) {
            player.teleToLocation(-59004, -56889, -2032);
        } else if (event.startsWith("start_invasion")) {
            if (isUnderInvasion) {
                return "";
            }
            isUnderInvasion = true;

            addStartNpc(NPC_ID);
            addTalkId(NPC_ID);
            addFirstTalkId(NPC_ID);

            _teleporter = addSpawn(NPC_ID, 147448, 27928, -2271, 20352, false, 0);
            _teleporter.setTitle("Creature Invasion");

            int radius = 1000;
            for (int a = 0; a < 2; a++) {
                for (int i = 0; i < 50; i++) {
                    int x = (int) (radius * Math.cos(i * 0.618));
                    int y = (int) (radius * Math.sin(i * 0.618));

                    Npc inv = addSpawn(invaderIds[Rnd.get(invaderIds.length)], -59718 + x, -56909 + y, -2029 + 20, -1, false, 0, false, 0);
                    invaders.add(inv);
                }
                radius += 300;
            }

            Broadcast.toAllOnlinePlayers("Fantasy Island is under invasion!");
            Broadcast.toAllOnlinePlayers("Don't attack mobs from other players!");
            Broadcast.toAllOnlinePlayers("Dualbox is not allowed on the event!");
            Broadcast.toAllOnlinePlayers("The invasion will lasts for: " + timeToEndInvasion + " minute(s)!");

            startQuestTimer("end_invasion", timeToEndInvasion * 60000, null, null);
        } else if (event.startsWith("end_invasion")) {
            isUnderInvasion = false;

            if (_teleporter != null) {
                _teleporter.deleteMe();
            }

            for (Creature chara : invaders) {
                if (chara == null) {
                    continue;
                }
                chara.deleteMe();
            }

            invaders.clear();
            attackInfo.clear();

            Broadcast.toAllOnlinePlayers("The invasion has been ended!");
        }
        return "";
    }

    @Override
    public String onFirstTalk(Npc npc, PlayerInstance player) {
        StringBuilder tb = new StringBuilder();
        tb.append("<html><center><font color=\"3D81A8\">Creature Invasion</font></center><br1>Hi " + player.getName() + "<br>");
        tb.append("<font color=\"3D81A8\">Available Actons:</font><br>");
        tb.append("<br>");
        tb.append("   <center><button value=\"Teleport to Fantasy Island\" action=\"bypass -h gm_event creature_invasion teleport_to_fantasy\"\n" +
                "                            width=200\n" +
                "                            height=40 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_CT1.Button_DF\"></center>\n" +
                "               ");
        tb.append("</body></html>");
        NpcHtmlMessage msg = new NpcHtmlMessage(NPC_ID);
        msg.setHtml(tb.toString());
        player.sendPacket(msg);
        return "";
    }

    public static CreatureInvasion getInstance() {
        return CreatureInvasion.SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        protected static final CreatureInvasion INSTANCE = new CreatureInvasion(999120);
    }

}<file_sep>/data/html/villagemaster/SubClass_ChangeNo.htm
<html>
<body>Change Subclass:<br>
You can't change subclasses when you don't have a subclass to begin with.<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Subclass 1">Add subclass.</Button>
</body>
</html><file_sep>/java/org/l2jmobius/gameserver/events/instanced/EventTeleporter.java
package org.l2jmobius.gameserver.events.instanced;

import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.PetInstance;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.skills.AbnormalVisualEffect;
import org.l2jmobius.gameserver.util.Point3D;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.events.instanced.EventInstance.EventState;
import org.l2jmobius.gameserver.events.instanced.EventInstance.EventType;

public class EventTeleporter implements Runnable {
    private PlayerInstance playerInstance = null;
    private Point3D coordinates = null;
    private boolean restore = false;
    private boolean heal = true;

    public EventTeleporter(PlayerInstance playerInstance, Point3D coordinates, boolean fastSchedule, boolean restore) {
        this.playerInstance = playerInstance;
        this.coordinates = coordinates;
        this.restore = restore;

        long delay = (playerInstance.getEvent() == null || playerInstance.getEvent().isState(EventState.STARTED) ?
                Config.INSTANCED_EVENT_RESPAWN_TELEPORT_DELAY  :  Config.INSTANCED_EVENT_START_LEAVE_TELEPORT_DELAY ) * 1000;

        ThreadPool.schedule(this, fastSchedule ? 0 : delay);
    }

    public EventTeleporter(PlayerInstance playerInstance, Point3D coordinates, boolean fastSchedule, boolean restore, boolean heal) {
        this.playerInstance = playerInstance;
        this.coordinates = coordinates;
        this.restore = restore;
        this.heal = heal;

        long delay = (playerInstance.getEvent() == null || playerInstance.getEvent().isState(EventState.STARTED) ?
                 Config.INSTANCED_EVENT_RESPAWN_TELEPORT_DELAY : Config.INSTANCED_EVENT_START_LEAVE_TELEPORT_DELAY) * 1000;

        ThreadPool.schedule(this, fastSchedule ? 0 : delay);
    }

    @Override
    public void run() {
        if (playerInstance == null) {
            return;
        }

        EventInstance event = playerInstance.getEvent();
        if (event == null) {
            return;
        }

        try {
            playerInstance.stopAllEffects();

            PetInstance pet = playerInstance.getPet();
            if (pet != null) {
                // In LC, SS and SS2, players don't need summons and summons are even able to attack during event so better unsummon
                if (pet.isMountable() || event.isType(EventType.LuckyChests) || event.isType(EventType.StalkedSalkers) ||
                        event.isType(EventType.SimonSays)) {
                    pet.unSummon(playerInstance);
                } else {
                    pet.stopAllEffects();
                }
            }

            if (event.getConfig().isAllVsAll()) {
                playerInstance.leaveParty();
            }

            for (Summon summon : playerInstance.getServitorsAndPets()) {
                // In LC, SS and SS2, players don't need summons and summons are even able to attack during event so better unsummon
                if (event.isType(EventType.LuckyChests) || event.isType(EventType.StalkedSalkers) || event.isType(EventType.SimonSays)) {
                    summon.unSummon(playerInstance);
                } else {
                    summon.stopAllEffects();
                }
            }

            if (playerInstance.isDead()) {
                playerInstance.restoreExp(100.0);
                playerInstance.doRevive();
            }

            if (heal) {
                playerInstance.setCurrentCp(playerInstance.getMaxCp());
                playerInstance.setCurrentHp(playerInstance.getMaxHp());
                playerInstance.setCurrentMp(playerInstance.getMaxMp());
            }

            int x = 0, y = 0, z = 0;
            if (event.isState(EventState.STARTED) && !restore) {
                playerInstance.setInstanceId(event.getInstanceId());
                if (event.getConfig().spawnsPlayersRandomly()) {
                    EventLocation location = event.getConfig().getLocation();
                    Location pos = location.getZone().getZone().getRandomPoint();
                    x = pos.getX();
                    y = pos.getY();
                    z = GeoEngine.getInstance().getHeight(pos.getX(), pos.getY(), pos.getZ());
                } else {
                    float r1 = Rnd.get(1000);
                    int r2 = Rnd.get(100);
                    x = Math.round((float) Math.cos(r1 / 1000 * 2 * Math.PI) * r2 + coordinates.getX());
                    y = Math.round((float) Math.sin(r1 / 1000 * 2 * Math.PI) * r2 + coordinates.getY());
                    z = GeoEngine.getInstance().getHeight(x, y, coordinates.getZ());
                }
            } else {
                playerInstance.setInstanceId(0);
                playerInstance.setEvent(null);
                playerInstance.returnedFromEvent();
                if (playerInstance.getEventSavedPosition().getX() == 0 && playerInstance.getEventSavedPosition().getY() == 0 &&
                        playerInstance.getEventSavedPosition().getZ() == 0) {
                    x = coordinates.getX();
                    y = coordinates.getY();
                    z = GeoEngine.getInstance().getHeight(coordinates.getX(), coordinates.getY(), coordinates.getZ());
                } else {
                    x = playerInstance.getEventSavedPosition().getX();
                    y = playerInstance.getEventSavedPosition().getY();
                    z = playerInstance.getEventSavedPosition().getZ();
                }
            }
            playerInstance.teleToLocation(x, y, z);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
<file_sep>/config/Custom/FindPvP.ini
# ---------------------------------------------------------------------------
# Find PvP
# ---------------------------------------------------------------------------
# Bypass example:
# <Button ALIGN=LEFT ICON="NORMAL" action="bypass -h FindPvP">Go to PvP!</Button>

# Enable FindPvP bypass.
# Default: False
EnableFindPvP = True
<file_sep>/java/org/l2jmobius/gameserver/gui/playertable/PlayerTableRenderer.java
package org.l2jmobius.gameserver.gui.playertable;

import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import java.awt.*;

public class PlayerTableRenderer extends DefaultTableCellRenderer implements TableCellRenderer {
    private static final long serialVersionUID = 1L;
    @SuppressWarnings("unused")
    private PlayerTableModel table;

    public PlayerTableRenderer(PlayerTableModel table) {
        this.table = table;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
        Component c;
        if (value instanceof Component) {
            c = (Component) value;
            if (isSelected) {
                c.setForeground(table.getSelectionForeground());
                c.setBackground(table.getSelectionBackground());
            }
        } else {
            c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
        }

        if (!isSelected) {
            c.setBackground(table.getBackground());
        }

        return c;
    }

    public interface TooltipTable {
        String getToolTip(int row, int col);

        boolean getIsMarked(int row);
    }
}<file_sep>/data/scripts/handlers/voicedcommandhandlers/Zone.java
/*
 * This file is part of the L2J Mobius project.
 * 
 * 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 scripts.handlers.voicedcommandhandlers;

import org.l2jmobius.gameserver.handler.IVoicedCommandHandler;
import org.l2jmobius.gameserver.instancemanager.UpgradableFarmZoneManager;
import org.l2jmobius.gameserver.instancemanager.ZoneManager;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.stats.Stat;
import org.l2jmobius.gameserver.model.zone.ZoneId;
import org.l2jmobius.gameserver.model.zone.ZoneType;
import org.l2jmobius.gameserver.model.zone.type.FarmZone;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;

import java.text.DecimalFormat;

public class Zone implements IVoicedCommandHandler
{
	private static final String[] VOICED_COMMANDS =
	{
		"zone"
	};
	
	@Override
	public boolean useVoicedCommand(String command, PlayerInstance player, String target)
	{
		if (command.startsWith("zone"))
		{
			if (player.isInsideZone(ZoneId.FARM)) {
				ZoneType zone = ZoneManager.getInstance().getZone(player.getLocation(), FarmZone.class);
				if (zone != null) {
					UpgradableFarmZoneManager.getInstance().getPanel(player, "farm_zone;" + String.valueOf(zone.getId()));
				}
			}
		}
		else
		{
			return false;
		}
		return true;
	}


	@Override
	public String[] getVoicedCommandList()
	{
		return VOICED_COMMANDS;
	}
}<file_sep>/data/scripts/handlers/bypasshandlers/Teleport.java
/*
 * This file is part of the L2J Mobius project.
 * 
 * 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 scripts.handlers.bypasshandlers;

import org.l2jmobius.Config;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.handler.IBypassHandler;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.instance.MerchantInstance;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.CreatureSay;
import org.l2jmobius.gameserver.network.serverpackets.SystemMessage;

import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Level;

public class Teleport implements IBypassHandler
{
	private static final String[] COMMANDS =
	{
			"teleto",
			"pvpzone"
	};
	
	@Override
	public boolean useBypass(String command, PlayerInstance player, Creature target)
	{
		if (!(target instanceof TeleporterInstance))
		{
			return false;
		}


		StringTokenizer st = new StringTokenizer(command, " ");
		st.nextToken();

		if (command.startsWith("teleto")) // Tenkai custom - raw teleport coordinates, only check for TW ward
		{
			if (player.isCombatFlagEquipped())
			{
				player.sendPacket(SystemMessageId.YOU_CANNOT_TELEPORT_WHILE_IN_POSSESSION_OF_A_WARD);
				return false;
			}

			if (player.getPvpFlag() > 0)
			{
				player.sendMessage("You can't teleport while flagged!");
				return false;
			}

			int[] coords = new int[3];
			try
			{
				for (int i = 0; i < 3; i++)
				{
					coords[i] = Integer.valueOf(st.nextToken());
				}
				player.teleToLocation(coords[0], coords[1], coords[2]);
				player.setInstanceId(0);
			}
			catch (Exception e)
			{
				LOGGER.warning("L2Teleporter - " + target.getName() + "(" + target.getId() +
						") - failed to parse raw teleport coordinates from html");
				e.printStackTrace();
			}

			return true;
		}


		return false;
	}
	
	@Override
	public String[] getBypassList()
	{
		return COMMANDS;
	}
}
<file_sep>/java/org/l2jmobius/gameserver/model/actor/instance/BufferInstance.java
/*
 * This file is part of the L2J Mobius project.
 * 
 * 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 org.l2jmobius.gameserver.model.actor.instance;

import org.l2jmobius.Config;
import org.l2jmobius.gameserver.data.xml.impl.BuyListData;
import org.l2jmobius.gameserver.data.xml.impl.SkillData;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.enums.TaxType;
import org.l2jmobius.gameserver.instancemanager.CastleManager;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.buylist.ProductList;
import org.l2jmobius.gameserver.model.entity.Castle;
import org.l2jmobius.gameserver.model.itemcontainer.Inventory;
import org.l2jmobius.gameserver.model.items.Item;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.olympiad.OlympiadManager;
import org.l2jmobius.gameserver.model.skills.EffectScope;
import org.l2jmobius.gameserver.model.skills.Skill;
import org.l2jmobius.gameserver.model.zone.ZoneId;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.*;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @version $Revision: 1.10.4.9 $ $Date: 2005/04/11 10:06:08 $
 */
public class BufferInstance extends NpcInstance
{
	private static final int[] buffs = { 14791, 14792, 14793, 14794, 30814, 14788, 14789, 14790, 17175, 17176, 17177};

	public BufferInstance(NpcTemplate template)
	{
		super(template);
		setInstanceType(InstanceType.BufferInstance);
	}

	@Override
	public void onBypassFeedback(PlayerInstance player, String command)
	{
		if (player == null)
		{
			return;
		}

		if (player.getEvent() != null)
		{
			player.sendMessage("I can not help you if you are registered for an event");
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}

		if (AttackStanceTaskManager.getInstance().hasAttackStanceTask(player))
		{
			player.sendMessage("I can not help you while you are fighting");
			player.sendPacket(ActionFailed.STATIC_PACKET);
			return;
		}

		if (command.startsWith("BuffMe"))
		{
			if (player.isInCombat() || player.isDead() || player.isInOlympiadMode() || player.getPvpFlag() > 0 ||
					OlympiadManager.getInstance().isRegisteredInComp(player) || player.isPlayingEvent())
			{
				player.sendMessage("You can't use this option now!");
				return;
			}

			StringTokenizer st = new StringTokenizer(command, " ");
			st.nextToken();
			int skillId = Integer.valueOf(st.nextToken());

			if (skillId < 4)
			{
				int i = 0;
				for (int id : buffs)
				{
					int level = 1;
					if (i < 4) {
						level = 4;
					}
					giveBuff(player, id, level);
					i++;
				}


			}
			else
			{
				giveBuff(player, skillId, 1);
			}

			showChatWindow(player, 2);
		}
		else if (command.startsWith("Buff"))
		{
			StringTokenizer st = new StringTokenizer(command.substring(5), " ");
			int buffId = Integer.parseInt(st.nextToken());
			int chatPage = Integer.parseInt(st.nextToken());
			int buffLevel = SkillData.getInstance().getMaxLevel(buffId);

			Skill skill = SkillData.getInstance().getSkill(buffId, buffLevel);
			if (skill != null)
			{
				skill.applyEffects(player, player);
				player.setCurrentMp(player.getMaxMp());
			}

			showChatWindow(player, chatPage);
		}
		else if (command.startsWith("Heal"))
		{
			if ((player.isInCombat() || player.getPvpFlag() > 0) && !player.isInsideZone(ZoneId.PEACE))
			{
				player.sendMessage("You cannot be healed while engaged in combat.");
				return;
			}

			player.setCurrentHp(player.getMaxHp());
			player.setCurrentMp(player.getMaxMp());
			player.setCurrentCp(player.getMaxCp());

			for (Summon summon : player.getServitorsAndPets())
			{
				summon.setCurrentHp(summon.getMaxHp());
				summon.setCurrentMp(summon.getMaxMp());
				summon.setCurrentCp(summon.getMaxCp());
			}

			showChatWindow(player);
		}
		else if (command.startsWith("RemoveBuffs"))
		{
			player.stopAllEffectsExceptThoseThatLastThroughDeath();
			showChatWindow(player, 0);
		}
		else if (command.startsWith("Pet") && player.getPet() == null && player.getServitorsAndPets().isEmpty())
		{
			player.sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
		}
		else if (command.startsWith("PetBuff"))
		{
			StringTokenizer st = new StringTokenizer(command.substring(8), " ");
			int buffId = Integer.parseInt(st.nextToken());
			int chatPage = Integer.parseInt(st.nextToken());
			int buffLevel = SkillData.getInstance().getMaxLevel(buffId);

			Skill skill = SkillData.getInstance().getSkill(buffId, buffLevel);
			if (skill != null)
			{
				if (player.getPet() != null)
				{
					skill.applyEffects(player, player.getPet());
					player.setCurrentMp(player.getMaxMp());
				}
				for (Summon summon : player.getServitorsAndPets())
				{
					skill.applyEffects(player, summon);
					player.setCurrentMp(player.getMaxMp());
				}
			}

			showChatWindow(player, chatPage);
		}
		else if (command.startsWith("PetHeal"))
		{
			if (player.getPet() != null)
			{
				player.getPet().setCurrentHp(player.getPet().getMaxHp());
				player.getPet().setCurrentMp(player.getPet().getMaxMp());
				player.getPet().setCurrentCp(player.getPet().getMaxCp());
			}
			for (Summon summon : player.getServitorsAndPets())
			{
				summon.setCurrentHp(summon.getMaxHp());
				summon.setCurrentMp(summon.getMaxMp());
				summon.setCurrentCp(summon.getMaxCp());
			}
			showChatWindow(player, 10);
		}
		else if (command.startsWith("PetRemoveBuffs"))
		{
			player.getPet().stopAllEffects();
			showChatWindow(player, 0);
		}
		else if (command.startsWith("Chat"))
		{
			showChatWindow(player, Integer.valueOf(command.substring(5)));
		}
		else
		{
			super.onBypassFeedback(player, command);
		}
	}

	private static void giveBuff(PlayerInstance player, int skillId, int level)
	{
		if (player == null)
		{
			return;
		}

		boolean buffSelf = true;
		boolean buffSummon = player.getTarget() != player;

		if (buffSummon)
		{
			if (player.getPet() != null)
			{
				SkillData.getInstance().getSkill(skillId, level).applyEffects(player.getPet(), player.getPet());
				player.getPet().setCurrentHpMp(player.getPet().getMaxHp(), player.getPet().getMaxMp());
				if (player.getTarget() == player.getPet())
				{
					buffSelf = false;
				}
			}

			if (player.getServitorsAndPets() != null)
			{
				for (Summon summon : player.getServitorsAndPets())
				{
					if (summon == null)
					{
						continue;
					}

					SkillData.getInstance().getSkill(skillId, level).applyEffects(summon, summon);
					summon.setCurrentHpMp(summon.getMaxHp(), summon.getMaxMp());
					if (player.getTarget() == summon)
					{
						buffSelf = false;
					}
				}
			}
		}

		if (buffSelf)
		{
			SkillData.getInstance().getSkill(skillId, level).applyEffects(player, player);
			player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp());
		}
	}

	public static void buff(PlayerInstance character)
	{
		int type = 2;
		if (character instanceof PlayerInstance)
		{
			PlayerInstance player = (PlayerInstance) character;
			if (!player.isMageClass())
			{
				ItemInstance shield = player.getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
				if (shield != null && shield.getItem().getType1() == Item.TYPE1_SHIELD_ARMOR)
				{
					type = 0;
				}
				else
				{
					type = 1;
				}
			}
		}
		else
		{
			type = 1;
		}

		for (int buff : buffs)
		{
			if (buff == 15649 && type != 0 || buff == 15650 && type != 1 || buff == 15648 && type != 2)
			{
				continue;
			}

			SkillData.getInstance().getSkill(buff, 1).applyEffects(character, character);
			character.setCurrentMp(character.getMaxMp());
		}
	}

	@Override
	public String getHtmlPath(int npcId, int value, PlayerInstance player)
	{
		String pom;
		if (value == 0)
		{
			pom = Integer.toString(npcId);
		}
		else
		{
			pom = npcId + "-" + value;
		}
		return "data/html/buffer/" + pom + ".htm";
	}

	@Override
	public void showChatWindow(PlayerInstance player, int val)
	{
		if (val >= 10 && player.getPet() == null && player.getServitorsAndPets().isEmpty())
		{
			val = 0;
		}
		// Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
		NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
		html.setFile(player, getHtmlPath(getId(), val, player));
		html.replace("%objectId%", String.valueOf(getObjectId()));
		player.sendPacket(html);

		// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
		player.sendPacket(ActionFailed.STATIC_PACKET);
	}


}
<file_sep>/config/Custom/PasswordChange.ini
# ---------------------------------------------------------------------------
# Password Change
# ---------------------------------------------------------------------------

# Enables .changepassword voiced command which allows the players to change their account's password ingame.
# Default: False
AllowChangePassword = True
<file_sep>/java/org/l2jmobius/commons/util/xml/XmlNode.kt
package l2server.util.xml

import org.w3c.dom.Node
import java.util.ArrayList
import java.util.function.Function
import kotlin.collections.HashMap
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set

/**
 * @author Pere
 */
class XmlNode internal constructor(private val base: Node) {

	val name: String = base.nodeName
	
	val text: String? = base.firstChild?.nodeValue

	val firstChild: XmlNode?
		get() {
			val children = getChildren()
			return if (children.isEmpty()) {
				null
			} else children[0]
		}

	fun hasAttributes() = base.attributes.length > 0

	fun hasAttribute(name: String) = base.attributes.getNamedItem(name) != null

	private fun getAttributeValue(name: String): String? = base.attributes.getNamedItem(name)?.nodeValue

	private fun <T> parse(name: String, value: String?, expectedType: Class<T>, parseFunction: Function<String, T>): T {
		if (value == null) {
			throw IllegalArgumentException(expectedType.simpleName + " value required for \"" + name + "\", but not specified.\r\nNode: " + this)
		}
		try {
			return parseFunction.apply(value)
		} catch (e: Exception) {
			throw IllegalArgumentException(expectedType.simpleName + " value required for \"" + name + "\", but found: " + value)
		}

	}

	private fun <T> parse(name: String, value: String?, expectedType: Class<T>, parseFunction: Function<String, T>, default: T): T {
		if (value == null) {
			return default
		}
		try {
			return parseFunction.apply(value)
		} catch (e: Exception) {
			throw IllegalArgumentException(expectedType.simpleName + " value required for \"" + name + "\", but found: " + value)
		}

	}

	fun getBool(name: String): Boolean {
		return parse(name, getAttributeValue(name), Boolean::class.java, Function { java.lang.Boolean.parseBoolean(it) })
	}

	fun getBool(name: String, default: Boolean): Boolean {
		return parse(name, getAttributeValue(name), Boolean::class.java, Function { java.lang.Boolean.parseBoolean(it) }, default)
	}

	fun getInt(name: String): Int {
		return parse(name, getAttributeValue(name), Int::class.java, Function { Integer.parseInt(it) })
	}

	fun getInt(name: String, default: Int): Int {
		return parse(name, getAttributeValue(name), Int::class.java, Function { Integer.parseInt(it) }, default)
	}

	fun getLong(name: String): Long {
		return parse(name, getAttributeValue(name), Long::class.java, Function { java.lang.Long.parseLong(it) })
	}

	fun getLong(name: String, default: Long): Long {
		return parse(name, getAttributeValue(name), Long::class.java, Function { java.lang.Long.parseLong(it) }, default)
	}

	fun getFloat(name: String): Float {
		return parse(name, getAttributeValue(name), Float::class.java, Function { java.lang.Float.parseFloat(it) })
	}

	fun getFloat(name: String, default: Float): Float {
		return parse(name, getAttributeValue(name), Float::class.java, Function { java.lang.Float.parseFloat(it) }, default)
	}

	fun getDouble(name: String): Double {
		return parse(name, getAttributeValue(name), Double::class.java, Function { java.lang.Double.parseDouble(it) })
	}

	fun getDouble(name: String, default: Double): Double {
		return parse(name, getAttributeValue(name), Double::class.java, Function { java.lang.Double.parseDouble(it) }, default)
	}

	fun getString(name: String): String {
		return parse(name, getAttributeValue(name), String::class.java, Function { it })
	}

	fun getString(name: String, default: String): String {
		return parse(name, getAttributeValue(name), String::class.java, Function { it }, default)
	}

	fun getAttributes(): Map<String, String> {
		val attributes = HashMap<String, String>()
		for (i in 0 until base.attributes.length) {
			val name = base.attributes.item(i).nodeName
			val value = base.attributes.item(i).nodeValue
			attributes[name] = value
		}
		return attributes
	}

	fun getChildren(): List<XmlNode> {
		val children = ArrayList<XmlNode>()
		var baseSubNode: Node? = base.firstChild
		while (baseSubNode != null) {
			if (baseSubNode.nodeType == Node.ELEMENT_NODE) {
				val child = XmlNode(baseSubNode)
				children.add(child)
			}
			baseSubNode = baseSubNode.nextSibling
		}
		return children
	}

	fun getChildren(name: String): List<XmlNode> {
		val list = ArrayList<XmlNode>()
		for (node in getChildren()) {
			if (node.name == name) {
				list.add(node)
			}
		}

		return list
	}

	fun getChild(name: String): XmlNode? {
		val children = getChildren(name)
		return if (children.isEmpty()) {
			null
		} else children[0]

	}

	override fun toString(): String {
		return toString(0)
	}

	private fun toString(tabDepth: Int): String {
		val tabsBuilder = StringBuilder()
		for (i in 0 until tabDepth) {
			tabsBuilder.append("\t")
		}

		val tabs = tabsBuilder.toString()
		val result = StringBuilder("$tabs<$name")
		for ((key, value) in getAttributes()) {
			result.append(" ").append(key).append("=\"").append(value).append("\"")
		}

		val children = getChildren()
		if (!children.isEmpty() || text != null && text.isNotEmpty()) {
			result.append(">\r\n")
			for (child in children) {
				result.append(child.toString(tabDepth + 1)).append("\r\n")
			}

			result.append(tabs).append("<").append(name).append(">\r\n")
		} else {
			result.append(" />")
		}

		return result.toString()
	}
}
<file_sep>/config/Custom/ClassBalance.ini
# ---------------------------------------------------------------------------
# Class Balance
# ---------------------------------------------------------------------------
# Multiply parameters based on player class. May use both class id or enums.
# Example: ELVEN_FIGHTER*2;PALUS_KNIGHT*2.5;...

PveMagicalSkillDamageMultipliers = 189*2.5;168*1.5;166*1.5;167*1.5;170*1.5;169*1.5;179*2;180*2;181*2;
PvpMagicalSkillDamageMultipliers = 189*2.5;176*2;177*2;178*2;

PveMagicalSkillDefenceMultipliers = 
PvpMagicalSkillDefenceMultipliers = 

PveMagicalSkillCriticalChanceMultipliers = 
PvpMagicalSkillCriticalChanceMultipliers = 

PveMagicalSkillCriticalDamageMultipliers = 
PvpMagicalSkillCriticalDamageMultipliers = 

PvePhysicalSkillDamageMultipliers = 
PvpPhysicalSkillDamageMultipliers = 

PvePhysicalSkillDefenceMultipliers = 
PvpPhysicalSkillDefenceMultipliers = 

PvePhysicalSkillCriticalChanceMultipliers = 
PvpPhysicalSkillCriticalChanceMultipliers = 

PvePhysicalSkillCriticalDamageMultipliers = 
PvpPhysicalSkillCriticalDamageMultipliers = 

PvePhysicalAttackDamageMultipliers = 152*1.2;157*1;154*1;153*1.5;156*2;155*1.5;174*2;175*2;176*2;177*2;
PvpPhysicalAttackDamageMultipliers = 

PvePhysicalAttackDefenceMultipliers = 
PvpPhysicalAttackDefenceMultipliers = 

PvePhysicalAttackCriticalChanceMultipliers = 
PvpPhysicalAttackCriticalChanceMultipliers = 

PvePhysicalAttackCriticalDamageMultipliers = 
PvpPhysicalAttackCriticalDamageMultipliers = 

PveBlowSkillDamageMultipliers = 158*1;161*1;160*1;159*1; 
PvpBlowSkillDamageMultipliers =

PveEnergySkillDamageMultipliers =
PvpEnergySkillDamageMultipliers =

PveEnergySkillDefenceMultipliers =
PvpEnergySkillDefenceMultipliers =

PveBlowSkillDefenceMultipliers = 
PvpBlowSkillDefenceMultipliers = 

PlayerHealingSkillMultipliers = 

SkillMasteryChanceMultipliers = 

ExpAmountMultipliers = 
SpAmountMultipliers = 
<file_sep>/java/org/l2jmobius/gameserver/events/instanced/EventInstance.java
package org.l2jmobius.gameserver.events.instanced;

import org.l2jmobius.Config;
import org.l2jmobius.commons.concurrent.ThreadPool;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.datatables.EventPrizesTable;
import org.l2jmobius.gameserver.enums.ChatType;
import org.l2jmobius.gameserver.enums.PartyDistributionType;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.gameserver.instancemanager.InstanceManager;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.Party;
import org.l2jmobius.gameserver.model.actor.Creature;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.olympiad.OlympiadManager;
import org.l2jmobius.gameserver.model.zone.ZoneId;
import org.l2jmobius.gameserver.network.clientpackets.Say2;
import org.l2jmobius.gameserver.network.serverpackets.*;
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
import org.l2jmobius.gameserver.util.Broadcast;
import org.l2jmobius.gameserver.util.Point3D;
import org.l2jmobius.loginserver.network.GameServerPacketHandler;

import java.util.List;
import java.util.Random;

public abstract class EventInstance {
    public enum EventType {
        TVT,
        LuckyChests,
        CaptureTheFlag,
        SimonSays,
        VIP,
        Survival,
        DeathMatch,
        KingOfTheHill,
        TeamSurvival,
        CursedBattle,
        DestroyTheGolem,
        FieldDomination,
        StalkedSalkers
    }

    public enum EventState {
        INACTIVE,
        READY,
        STARTED,
        REWARDING
    }

    protected final EventConfig config;
    protected final int id;
    protected final int instanceId;

    protected EventTeam[] teams = new EventTeam[4];
    protected EventState state = EventState.INACTIVE;

    private int participants;

    private long startTime = 0;

    public int randomBetween(int low, int high) {
        Random r = new Random();
        return r.nextInt(high-low) + low;
    }

    public EventInstance(int id, EventConfig config) {
        this.id = id;
        this.config = config;
        teams[0] = new EventTeam(0, config.getTeamName(0), config.getLocation().getSpawn(0));
        teams[1] = new EventTeam(1, config.getTeamName(1), config.getLocation().getSpawn(1));
        teams[2] = new EventTeam(2, config.getTeamName(2), config.getLocation().getSpawn(2));
        teams[3] = new EventTeam(3, config.getTeamName(3), config.getLocation().getSpawn(3));

        instanceId = id + 40000;
        InstanceManager.getInstance().createInstance();
        setState(EventState.READY);

        ThreadPool.schedule(() -> sendToAllParticipants("Match found! The event is starting in 20 seconds."), 5000L);

        ThreadPool.schedule(this::startFight, 20000L);
    }

    public boolean startFight() {
        for (EventTeam team : teams) {
            for (PlayerInstance player : team.getParticipatedPlayers().values()) {
                if (player != null &&
                        (OlympiadManager.getInstance().isRegisteredInComp(player) || player.isInOlympiadMode() || player.isOlympiadStart() ||
                                player.isFlyingMounted() || player.inObserverMode())) {
                    removeParticipant(player.getObjectId());
                }
            }
        }

        if (id != 100) {
            // Check for enough participants
            if (!config.isAllVsAll()) {
                if (config.getLocation().getTeamCount() != 4) {
                    if (teams[0].getParticipatedPlayerCount() < Config.INSTANCED_EVENT_MIN_PLAYERS_IN_TEAMS  ||
                            teams[1].getParticipatedPlayerCount() <  Config.INSTANCED_EVENT_MIN_PLAYERS_IN_TEAMS ) {
                        // Set state INACTIVE
                        setState(EventState.INACTIVE);
                        // Cleanup of teams
                        teams[0].onEventNotStarted();
                        teams[1].onEventNotStarted();
                        return false;
                    }
                } else {
                    if (teams[0].getParticipatedPlayerCount() < Config.INSTANCED_EVENT_MIN_PLAYERS_IN_TEAMS  ||
                            teams[1].getParticipatedPlayerCount() < Config.INSTANCED_EVENT_MIN_PLAYERS_IN_TEAMS ||
                            teams[2].getParticipatedPlayerCount() < Config.INSTANCED_EVENT_MIN_PLAYERS_IN_TEAMS ||
                            teams[3].getParticipatedPlayerCount() < Config.INSTANCED_EVENT_MIN_PLAYERS_IN_TEAMS ) {
                        // Set state INACTIVE
                        setState(EventState.INACTIVE);
                        // Cleanup of teams
                        teams[0].onEventNotStarted();
                        teams[1].onEventNotStarted();
                        teams[2].onEventNotStarted();
                        teams[3].onEventNotStarted();
                        return false;
                    }
                }
            } else {
                if (teams[0].getParticipatedPlayerCount() < 2) {
                    setState(EventState.INACTIVE);
                    teams[0].onEventNotStarted();
                    return false;
                }
                participants = teams[0].getParticipatedPlayerCount();
            }
        }

        // Iterate over all teams
        for (EventTeam team : teams) {
            int divider = 7;
            if (team.getParticipatedPlayerCount() > 2) {
                while (team.getParticipatedPlayerCount() % divider > 0 && team.getParticipatedPlayerCount() % divider <= 2 && divider > 5) {
                    divider--;
                }
            }

            int partyCount = team.getParticipatedPlayerCount() / divider;
            if (team.getParticipatedPlayerCount() % divider > 0) {
                partyCount++;
            }
            Party[] parties = new Party[partyCount];
            int currentParty = 0;
            // Iterate over all participated player instances in this team
            for (PlayerInstance playerInstance : team.getParticipatedPlayers().values()) {
                if (playerInstance != null) {
                    playerInstance.setEventPoints(0);

                    try {
                        playerInstance.eventSaveData();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (playerInstance.isMounted()) {
                        playerInstance.dismount();
                    }
                    // Teleporter implements Runnable and starts itself
                    new EventTeleporter(playerInstance, team.getCoords(), false, false);

                    playerInstance.leaveParty();
                    if (!config.isAllVsAll()) {
                        // Add the player into the current party or create it if it still doesn't exist
                        if (parties[currentParty] == null) {
                            parties[currentParty] = new Party(playerInstance, PartyDistributionType.FINDERS_KEEPERS);
                            playerInstance.setParty(parties[currentParty]);
                        } else {
                            playerInstance.joinParty(parties[currentParty]);
                        }

                        // Rotate current party index
                        currentParty++;
                        if (currentParty >= partyCount) {
                            currentParty = 0;
                        }
                    }
                }
            }
        }

        Broadcast.toAllOnlinePlayers("The " + config.getEventName() + " has started.");

        if (!config.isType(EventType.TeamSurvival) && !config.isType(EventType.Survival) && !config.isType(EventType.SimonSays)) {
            ThreadPool.schedule(this::stopFight, 60000 * Config.INSTANCED_EVENT_RUNNING_TIME );
        }

        // Set state STARTED
        setState(EventState.STARTED);
        startTime = System.currentTimeMillis();

        return true;
    }

    public long getStartTime() {
        return startTime;
    }

    public abstract void calculateRewards();

    protected void onContribution(PlayerInstance player, int weight) {
        if (player == null) {
        }

		/*
		int rewardId = 6392;
		int rewardQt = weight;
		player.addItem("Instanced Events", rewardId, rewardQt, player, true);
		StatusUpdate statusUpdate = new StatusUpdate(player);
		statusUpdate.addAttribute(StatusUpdate.CUR_LOAD, player.getCurrentLoad());
		player.sendPacket(statusUpdate);
		 */
    }

    protected void rewardTeams(int winnerTeam) {
        EventTeam winner = null;
        if (winnerTeam >= 0) {
            winner = teams[winnerTeam];
        }

        for (EventTeam team : teams) {
            int totalPoints = 0;
            for (PlayerInstance player : team.getParticipatedPlayers().values()) {
                if (player != null) {
                    totalPoints += player.getEventPoints();
                }
            }

            float teamMultiplier = 0.5f;
            if (team == winner) {
                if (config.getLocation().getTeamCount() == 4) {
                    teamMultiplier = 2.5f;
                } else {
                    teamMultiplier = 1.5f;
                }
            }

            for (PlayerInstance player : team.getParticipatedPlayers().values()) {
                if (player == null) {
                    continue;
                }

                if (team == winner) {
                    player.sendPacket(new CreatureSay(player, ChatType.PARTYROOM_ALL, "Instanced Events", "Your team has won!!!"));
                } else {
                    player.sendPacket(new CreatureSay(player, ChatType.PARTYROOM_ALL, "Instanced Events", "Your team has lost :("));
                }

                float performanceMultiplier = player.getEventPoints() * ((float) team.getParticipatedPlayerCount() / (float) totalPoints);
                String performanceString;
                if (player.getEventPoints() == 0) {
                    performanceMultiplier = 0;
                }
                if (performanceMultiplier < 0.35) {
                    performanceString = "horrible";
                } else if (performanceMultiplier < 0.8) {
                    performanceString = "bad";
                } else if (performanceMultiplier < 1.2) {
                    performanceString = "okay";
                } else if (performanceMultiplier < 1.8) {
                    performanceString = "good";
                } else if (performanceMultiplier < 3.0) {
                    performanceString = "very good";
                } else {
                    performanceString = "amazing";
                }
                player.sendPacket(new CreatureSay(player,
                        ChatType.PARTYROOM_ALL,
                        "Instanced Events",
                        "Your performance in this event has been " + performanceString + "."));

                EventPrizesTable.getInstance().rewardPlayer("InstancedEvents", player, teamMultiplier, performanceMultiplier);

                StatusUpdate statusUpdate = new StatusUpdate(player);
                player.sendPacket(statusUpdate);
            }
        }
    }

    protected void rewardPlayers(List<PlayerInstance> players) {
        if (players == null || players.isEmpty()) {
            return;
        }

        int totalPoints = 0;
        for (PlayerInstance player : players) {
            if (player != null) {
                totalPoints += player.getEventPoints();
            }
        }

        for (PlayerInstance player : players) {
            if (player == null) {
                continue;
            }

            // Avoid 0 division
            if (totalPoints <= 0) {
                totalPoints = 1;
            }

            float performanceMultiplier = player.getEventPoints() * ((float) participants / (float) totalPoints);
            EventPrizesTable.getInstance().rewardPlayer("InstancedEvents", player, 1.0f, performanceMultiplier);

            StatusUpdate statusUpdate = new StatusUpdate(player);

            NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(0);
            npcHtmlMessage.setHtml(
                    "<html><head><title>Instanced Events</title></head><body>The event has finished. Look at your inventory, there should be your reward.</body></html>");
            player.sendPacket(npcHtmlMessage);

            // TODO: Place this at the HTML
            player.sendPacket(new CreatureSay(player, ChatType.PARTYROOM_ALL, "Instanced Events", "Event score:"));
            for (PlayerInstance rewarded : players) {
                if (rewarded != null) {
                    player.sendPacket(new CreatureSay(player, ChatType.PARTYROOM_ALL,
                            "Instanced Events",
                            rewarded.getName() + ": " + rewarded.getEventPoints()));
                }
            }
        }
    }

    public void stopFight() {
        Broadcast.toAllOnlinePlayers("The " + config.getEventName() + " has ended.");
        calculateRewards();

        // Iterate over all teams
        for (EventTeam team : teams) {
            for (PlayerInstance playerInstance : team.getParticipatedPlayers().values()) {
                // Check for nullpointer
                if (playerInstance != null) {
                    if (playerInstance.getCtfFlag() != null) {
                        playerInstance.setCtfFlag(null);
                    }
                    playerInstance.setEventPoints(-1);
                    // TODO: EVENT: TELEPORT TO MAIN TOWN
                    new EventTeleporter(playerInstance, new Point3D(playerInstance.getEventSavedPosition().getX(), playerInstance.getEventSavedPosition().getY(), playerInstance.getEventSavedPosition().getZ()), true, true);
                }
                team.setVIP(null);
            }
        }

        // Cleanup of teams
        teams[0].cleanMe();
        teams[1].cleanMe();
        if (config.getLocation().getTeamCount() == 4) {
            teams[2].cleanMe();
            teams[3].cleanMe();
        }

        ThreadPool.schedule(() -> {
            // Set state INACTIVE
            setState(EventState.INACTIVE);
        }, 5000L);
    }

    public synchronized boolean addParticipant(PlayerInstance playerInstance) {
        // Check for nullpointer
        if (playerInstance == null) {
            return false;
        }

        playerInstance.setEvent(this);
        byte teamId = 0;
        if (config.isAllVsAll()) {
            return teams[teamId].addPlayer(playerInstance);
        }

        if (playerInstance.getClassId().getId() == 146) {
            if (config.getLocation().getTeamCount() == 2) {
                // Check to which team the player should be added
                if (teams[0].getHealersCount() == teams[1].getHealersCount()) {
                    teamId = (byte) Rnd.get(2);
                } else {
                    teamId = (byte) (teams[0].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() ? 1 : 0);
                }
            } else {
                int minHealers = 50;
                for (byte i = 0; i < 4; i++) {
                    if (teams[i].getHealersCount() < minHealers) {
                        teamId = i;
                        minHealers = teams[i].getHealersCount();
                    }
                }
            }
        } else if (config.getLocation().getTeamCount() == 2) {
            // Check to which team the player should be added
            if (teams[0].getParticipatedPlayerCount() == teams[1].getParticipatedPlayerCount()) {
                teamId = (byte) Rnd.get(2);
            } else {
                teamId = (byte) (teams[0].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() ? 1 : 0);
            }
        } else {
            // Check to which team the player should be added

            if (teams[0].getParticipatedPlayerCount() < teams[1].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() < teams[2].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() < teams[3].getParticipatedPlayerCount()) {
                teamId = (byte) 0;
            } else if (teams[1].getParticipatedPlayerCount() < teams[0].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() < teams[2].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() < teams[3].getParticipatedPlayerCount()) {
                teamId = (byte) 1;
            } else if (teams[2].getParticipatedPlayerCount() < teams[0].getParticipatedPlayerCount() &&
                    teams[2].getParticipatedPlayerCount() < teams[1].getParticipatedPlayerCount() &&
                    teams[2].getParticipatedPlayerCount() < teams[3].getParticipatedPlayerCount()) {
                teamId = (byte) 2;
            } else if (teams[3].getParticipatedPlayerCount() < teams[0].getParticipatedPlayerCount() &&
                    teams[3].getParticipatedPlayerCount() < teams[1].getParticipatedPlayerCount() &&
                    teams[3].getParticipatedPlayerCount() < teams[2].getParticipatedPlayerCount()) {
                teamId = (byte) 3;
            } else if (teams[0].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[2].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() == teams[3].getParticipatedPlayerCount()) {
                while (teamId == 0 || teamId == 2) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[0].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[3].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() == teams[2].getParticipatedPlayerCount()) {
                while (teamId == 0 || teamId == 3) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[0].getParticipatedPlayerCount() > teams[2].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() > teams[2].getParticipatedPlayerCount() &&
                    teams[2].getParticipatedPlayerCount() == teams[3].getParticipatedPlayerCount()) {
                while (teamId == 0 || teamId == 1) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[0].getParticipatedPlayerCount() < teams[1].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() < teams[3].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() == teams[2].getParticipatedPlayerCount()) {
                while (teamId == 1 || teamId == 3) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[0].getParticipatedPlayerCount() < teams[1].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() < teams[2].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() == teams[3].getParticipatedPlayerCount()) {
                while (teamId == 1 || teamId == 2) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[0].getParticipatedPlayerCount() < teams[2].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() < teams[3].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() == teams[1].getParticipatedPlayerCount()) {
                while (teamId == 2 || teamId == 3) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[0].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() > teams[2].getParticipatedPlayerCount() &&
                    teams[0].getParticipatedPlayerCount() > teams[3].getParticipatedPlayerCount()) {
                while (teamId == 0) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[1].getParticipatedPlayerCount() > teams[0].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() > teams[2].getParticipatedPlayerCount() &&
                    teams[1].getParticipatedPlayerCount() > teams[3].getParticipatedPlayerCount()) {
                while (teamId == 1) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[2].getParticipatedPlayerCount() > teams[0].getParticipatedPlayerCount() &&
                    teams[2].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[2].getParticipatedPlayerCount() > teams[3].getParticipatedPlayerCount()) {
                while (teamId == 2) {
                    teamId = (byte) Rnd.get(4);
                }
            } else if (teams[3].getParticipatedPlayerCount() > teams[0].getParticipatedPlayerCount() &&
                    teams[3].getParticipatedPlayerCount() > teams[1].getParticipatedPlayerCount() &&
                    teams[3].getParticipatedPlayerCount() > teams[2].getParticipatedPlayerCount()) {
                teamId = (byte) Rnd.get(3);
            } else {
                teamId = (byte) Rnd.get(4);
            }
        }

        return teams[teamId].addPlayer(playerInstance);
    }

    public boolean removeParticipant(int playerObjectId) {
        // Get the teamId of the player
        byte teamId = getParticipantTeamId(playerObjectId);

        // Check if the player is participant
        if (teamId != -1) {
            // Remove the player from team
            teams[teamId].removePlayer(playerObjectId);
            return true;
        }

        return false;
    }

    public void sendToAllParticipants(IClientOutgoingPacket packet) {
        for (PlayerInstance playerInstance : teams[0].getParticipatedPlayers().values()) {
            if (playerInstance != null) {
                playerInstance.sendPacket(packet);
            }
        }

        for (PlayerInstance playerInstance : teams[1].getParticipatedPlayers().values()) {
            if (playerInstance != null) {
                playerInstance.sendPacket(packet);
            }
        }

        if (config.getLocation().getTeamCount() == 4) {
            for (PlayerInstance playerInstance : teams[2].getParticipatedPlayers().values()) {
                if (playerInstance != null) {
                    playerInstance.sendPacket(packet);
                }
            }
            for (PlayerInstance playerInstance : teams[3].getParticipatedPlayers().values()) {
                if (playerInstance != null) {
                    playerInstance.sendPacket(packet);
                }
            }
        }
    }

    public void sendToAllParticipants(String message) {
        sendToAllParticipants(new CreatureSay(null, ChatType.PARTYROOM_ALL, "Instanced Events", message));
    }

    public void onLogin(PlayerInstance playerInstance) {
        if (playerInstance != null && isPlayerParticipant(playerInstance.getObjectId())) {
            removeParticipant(playerInstance.getObjectId());
            // TODO: EVENT:  Teleport port back to main town
            /*EventTeam team = getParticipantTeam(playerInstance.getObjectId());
			team.addPlayer(playerInstance);
			if (isState(EventState.STARTING) || isState(EventState.STARTED))
			{
				for (L2Effect effect : playerInstance.getAllEffects()) if (effect != null) effect.exit();
				new EventTeleporter(playerInstance, getParticipantTeam(playerInstance.getObjectId()).getCoords(), true, false);
			}*/
        }
    }

    public void onLogout(PlayerInstance playerInstance) {
        if (playerInstance != null && isPlayerParticipant(playerInstance.getObjectId())) {
            removeParticipant(playerInstance.getObjectId());
        }
    }

    public String getInfo(PlayerInstance player) {
        String html = "<center><font color=\"LEVEL\">" + config.getEventString() + "</font></center><br>";
        if (isState(EventState.READY)) {
            if (config.isAllVsAll()) {
                if (teams[0].getParticipatedPlayerCount() > 0) {
                    html += "Participants:<br>";
                    for (PlayerInstance participant : teams[0].getParticipatedPlayers().values()) {
                        if (participant != null) {
                            html += EventsManager.getInstance().getPlayerString(participant, player) + ", ";
                        }
                    }
                    html = html.substring(0, html.length() - 2) + ".";
                }
            } else {
                for (EventTeam team : teams) {
                    if (team.getParticipatedPlayerCount() > 0) {
                        html += "Team " + team.getName() + " participants:<br>";
                        for (PlayerInstance participant : team.getParticipatedPlayers().values()) {
                            if (participant != null) {
                                html += EventsManager.getInstance().getPlayerString(participant, player) + ", ";
                            }
                        }
                        html = html.substring(0, html.length() - 2) + ".<br>";
                    }
                }
                if (html.length() > 4) {
                    html = html.substring(0, html.length() - 4);
                }
            }
        } else if (isState(EventState.STARTED)) {
            html += getRunningInfo(player);
        } else if (isState(EventState.INACTIVE)) {
            html += "This event has ended.";
        }

        return html;
    }

    public abstract String getRunningInfo(PlayerInstance player);

    public void observe(PlayerInstance playerInstance) {
        if (playerInstance.getEvent() != null) {
            playerInstance.sendMessage("You cannot observe an event when you are participating on it.");
            playerInstance.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }
        if (AttackStanceTaskManager.getInstance().hasAttackStanceTask(playerInstance)) {
            playerInstance.sendMessage("You cannot observe an event while fighting.");
            playerInstance.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }
        if (playerInstance.isInsideZone(ZoneId.NO_SUMMON_FRIEND) || playerInstance.inObserverMode()) {
            playerInstance.sendMessage("You cannot observe an event from here.");
            playerInstance.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        int x;
        int y;
        int z;
        if (config.getLocation().getZone() == null) {
            int rndTm = Rnd.get(config.getLocation().getTeamCount());
            x = config.getLocation().getSpawn(rndTm).getX();
            y = config.getLocation().getSpawn(rndTm).getY();
            z = GeoEngine.getInstance().getHeight(x, y, config.getLocation().getGlobalZ());
        } else {
            Location pos = config.getLocation().getZone().getZone().getRandomPoint();
            x = pos.getX();
            y = pos.getY();
            z = GeoEngine.getInstance().getHeight(pos.getX(), pos.getY(), pos.getZ());
        }
        playerInstance.setInstanceId(instanceId);
        playerInstance.enterEventObserverMode(x, y, z);
    }

    public boolean onAction(PlayerInstance playerInstance, int targetPlayerObjectId) {
        EventTeam playerTeam = getParticipantTeam(playerInstance.getObjectId());
        EventTeam targetPlayerTeam = getParticipantTeam(targetPlayerObjectId);

        if (playerTeam == null || targetPlayerTeam == null) {
            return false;
        }

        return !(playerTeam == targetPlayerTeam && playerInstance.getObjectId() != targetPlayerObjectId &&
                !Config.INSTANCED_EVENT_TARGET_TEAM_MEMBERS_ALLOWED);
    }

    public boolean onForcedAttack(PlayerInstance playerInstance, int targetPlayerObjectId) {
        EventTeam playerTeam = getParticipantTeam(playerInstance.getObjectId());
        EventTeam targetPlayerTeam = getParticipantTeam(targetPlayerObjectId);

        if (playerTeam == null || targetPlayerTeam == null) {
            return false;
        }

        return !(playerTeam == targetPlayerTeam && playerInstance.getObjectId() != targetPlayerObjectId && config.isPvp());
    }

    public boolean onScrollUse(int playerObjectId) {
        if (!isState(EventState.STARTED)) {
            return true;
        }

        return !(isPlayerParticipant(playerObjectId) && !Config.INSTANCED_EVENT_SCROLL_ALLOWED);
    }

    public boolean onPotionUse(int playerObjectId) {
        if (!isState(EventState.STARTED)) {
            return true;
        }

        return !(isPlayerParticipant(playerObjectId) && !Config.INSTANCED_EVENT_POTIONS_ALLOWED);
    }

    public boolean onEscapeUse(int playerObjectId) {
        if (!isState(EventState.STARTED)) {
            return true;
        }

        return !isPlayerParticipant(playerObjectId);
    }

    public boolean onItemSummon(int playerObjectId) {
        if (!isState(EventState.STARTED)) {
            return true;
        }

        return !(isPlayerParticipant(playerObjectId) && !Config.INSTANCED_EVENT_SUMMON_BY_ITEM_ALLOWED);
    }

    public abstract void onKill(Creature killerCharacter, PlayerInstance killedPlayerInstance);

    public boolean isType(EventType type) {
        return config.getType() == type;
    }

    public EventType getType() {
        return config.getType();
    }

    public void setState(EventState state) {
        this.state = state;
    }

    public boolean isState(EventState state) {
        return this.state == state;
    }

    public byte getParticipantTeamId(int playerObjectId) {
        if (config.getLocation().getTeamCount() != 4) {
            return (byte) (teams[0].containsPlayer(playerObjectId) ? 0 : teams[1].containsPlayer(playerObjectId) ? 1 : -1);
        } else {
            return (byte) (teams[0].containsPlayer(playerObjectId) ? 0 : teams[1].containsPlayer(playerObjectId) ? 1 :
                    teams[2].containsPlayer(playerObjectId) ? 2 : teams[3].containsPlayer(playerObjectId) ? 3 : -1);
        }
    }

    public EventTeam getParticipantTeam(int playerObjectId) {
        if (config.getLocation().getTeamCount() != 4) {
            return teams[0].containsPlayer(playerObjectId) ? teams[0] : teams[1].containsPlayer(playerObjectId) ? teams[1] : null;
        } else {
            return teams[0].containsPlayer(playerObjectId) ? teams[0] : teams[1].containsPlayer(playerObjectId) ? teams[1] :
                    teams[2].containsPlayer(playerObjectId) ? teams[2] : teams[3].containsPlayer(playerObjectId) ? teams[3] : null;
        }
    }

    public EventTeam getParticipantEnemyTeam(int playerObjectId) {
        if (config.getLocation().getTeamCount() != 4) {
            return teams[0].containsPlayer(playerObjectId) ? teams[1] : teams[1].containsPlayer(playerObjectId) ? teams[0] : null;
        } else {
            return teams[0].containsPlayer(playerObjectId) ? teams[1] : teams[1].containsPlayer(playerObjectId) ? teams[0] :
                    teams[2].containsPlayer(playerObjectId) ? teams[3] : teams[3].containsPlayer(playerObjectId) ? teams[2] : null;
        }
    }

    public Point3D getParticipantTeamCoordinates(int playerObjectId) {
        if (config.getLocation().getTeamCount() != 4) {
            return teams[0].containsPlayer(playerObjectId) ? teams[0].getCoords() :
                    teams[1].containsPlayer(playerObjectId) ? teams[1].getCoords() : null;
        } else {
            return teams[0].containsPlayer(playerObjectId) ? teams[0].getCoords() : teams[1].containsPlayer(playerObjectId) ? teams[1].getCoords() :
                    teams[2].containsPlayer(playerObjectId) ? teams[2].getCoords() :
                            teams[3].containsPlayer(playerObjectId) ? teams[3].getCoords() : null;
        }
    }

    public boolean isPlayerParticipant(int playerObjectId) {
        if (!isState(EventState.STARTED)) {
            return false;
        }

        if (config.getLocation().getTeamCount() != 4) {
            return teams[0].containsPlayer(playerObjectId) || teams[1].containsPlayer(playerObjectId);
        } else {
            return teams[0].containsPlayer(playerObjectId) || teams[1].containsPlayer(playerObjectId) || teams[2].containsPlayer(playerObjectId) ||
                    teams[3].containsPlayer(playerObjectId);
        }
    }

    public int getParticipatedPlayersCount() {
        //if (!isState(EventState.PARTICIPATING) && !isState(EventState.STARTING) && !isState(EventState.STARTED))
        //	return 0;

        int count = 0;
        for (int teamId = 0; teamId < config.getLocation().getTeamCount(); teamId++) {
            count += teams[teamId].getParticipatedPlayerCount();
        }
        return count;
    }

    protected PlayerInstance selectRandomParticipant() {
        return teams[0].selectRandomParticipant();
    }

    public int getId() {
        return id;
    }

    public EventConfig getConfig() {
        return config;
    }

    public int getInstanceId() {
        return instanceId;
    }

    public void setImportant(PlayerInstance player, boolean important) {
        if (player == null) {
            return;
        }

        if (config.getLocation().getTeamCount() != 4 && config.isAllVsAll()) {
            player.setTeam(getParticipantTeamId(player.getObjectId()) + 1);
        } else {
            if (getParticipantTeamId(player.getObjectId()) == 0) {
                player.getAppearance().setNameColor(Integer.decode("0xFF0000"));
                player.getAppearance().setTitleColor(Integer.decode("0xFF0000"));
            }
            if (getParticipantTeamId(player.getObjectId()) == 1) {
                player.getAppearance().setNameColor(Integer.decode("0x0000FF"));
                player.getAppearance().setTitleColor(Integer.decode("0x0000FF"));
            }
            if (getParticipantTeamId(player.getObjectId()) == 2) {
                player.getAppearance().setNameColor(Integer.decode("0x00FFFF"));
                player.getAppearance().setTitleColor(Integer.decode("0x00FFFF"));
            }
            if (getParticipantTeamId(player.getObjectId()) == 3) {
                player.getAppearance().setNameColor(Integer.decode("0x00FF00"));
                player.getAppearance().setTitleColor(Integer.decode("0x00FF00"));
            }
            player.setTeam(0);
        }
        player.broadcastUserInfo();
        if (isType(EventType.LuckyChests) || isType(EventType.StalkedSalkers) || isType(EventType.SimonSays)) {
            player.disarmWeapons();
            player.setIsEventDisarmed(true);
        }
    }

    public EventTeam[] getTeams() {
        return teams;
    }
}
<file_sep>/config/Custom/OfflineTrade.ini
# ---------------------------------------------------------------------------
# Offline trade/craft
# ---------------------------------------------------------------------------

# Option to enable or disable offline trade feature.
# Enable -> true, Disable -> false
OfflineTradeEnable = True

# Option to enable or disable offline craft feature.
# Enable -> true, Disable -> false
OfflineCraftEnable = True

# If set to True, off-line shops will be possible only peace zones.
# Default: False
OfflineModeInPeaceZone = True

# If set to True, players in off-line shop mode wont take any damage, thus they cannot be killed.
# Default: False
OfflineModeNoDamage = True

# If set to True, name color will be changed then entering offline mode
OfflineSetNameColor = True

# Color of the name in offline mode (if OfflineSetNameColor = True)
OfflineNameColor = 808080

# Allow fame for characters in offline mode
# Enable -> true, Disable -> false
OfflineFame = True

#Restore offline traders/crafters after restart/shutdown. Default: false.
RestoreOffliners = True

#Do not restore offline characters, after OfflineMaxDays days spent from first restore.
#Require server restart to disconnect expired shops.
#0 = disabled (always restore).
#Default: 10
OfflineMaxDays = 0

#Disconnect shop after finished selling, buying.
#Default: True
OfflineDisconnectFinished = True

#Store offline trader transactions in realtime.
#Uses more datatabase resources, but helps if server shuts down unexpectedly.
StoreOfflineTradeInRealtime = True
<file_sep>/config/Custom/WalkerBotProtection.ini
# ---------------------------------------------------------------------------
# Walker/Bot protection
# ---------------------------------------------------------------------------

# Basic protection against L2Walker.
# Default: False
L2WalkerProtection = True
<file_sep>/out/production/Test/html/villagemaster/SubClass_Modify.htm
<html>
<body>Addition of a sub-class:<br>
Which of the following sub classes would you like to change?<br>
Sub-class 1<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Subclass 6 1">%sub1%</Button>
Sub-class 2<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Subclass 6 2">%sub2%</Button>
Sub-class 3<br>
<Button ALIGN=LEFT ICON="NORMAL" action="bypass -h npc_%objectId%_Subclass 6 3">%sub3%</Button>
<br><br>
If you change a sub-class, you'll start at level 40 after the 2nd class transfer.
</body>
</html><file_sep>/java/org/l2jmobius/gameserver/events/instanced/EventTeam.java
package org.l2jmobius.gameserver.events.instanced;

import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.datatables.SpawnTable;
import org.l2jmobius.gameserver.model.Spawn;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.util.Point3D;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class EventTeam {
    /**
     * The name of the team<br>
     */
    private String name;
    /**
     * The team spot coordinated<br>
     */
    private Point3D coordinates;
    /**
     * The points of the team<br>
     */
    private short points;
    /**
     * Name and instance of all participated players in HashMap<br>
     */
    private Map<Integer, PlayerInstance> participatedPlayers = new LinkedHashMap<>();

    private int flagId = 44004;
    private Spawn flagSpawn;
    private int golemId = 44003;
    private Spawn golemSpawn;

    private PlayerInstance VIP;

    /**
     * C'tor initialize the team<br><br>
     *
     * @param name        as String<br>
     * @param coordinates as int[]<br>
     */
    public EventTeam(int id, String name, Point3D coordinates) {
        flagId = 9009113 + id;
        this.name = name;
        this.coordinates = coordinates;
        points = 0;
        flagSpawn = null;
    }

    /**
     * Adds a player to the team<br><br>
     *
     * @param playerInstance as Player<br>
     * @return boolean: true if success, otherwise false<br>
     */
    public boolean addPlayer(PlayerInstance playerInstance) {
        if (playerInstance == null) {
            return false;
        }

        synchronized (participatedPlayers) {
            participatedPlayers.put(playerInstance.getObjectId(), playerInstance);
        }

        return true;
    }

    /**
     * Removes a player from the team<br><br>
     */
    public void removePlayer(int playerObjectId) {
        synchronized (participatedPlayers) {
			/*if (!EventsManager.getInstance().isType(EventType.DM)
                    && !EventsManager.getInstance().isType(EventType.SS)
					&& !EventsManager.getInstance().isType(EventType.SS2))
				participatedPlayers.get(playerObjectId).setEvent(null);*/
            participatedPlayers.remove(playerObjectId);
        }
    }

    /**
     * Increases the points of the team<br>
     */
    public void increasePoints() {
        ++points;
    }

    public void decreasePoints() {
        --points;
    }

    /**
     * Cleanup the team and make it ready for adding players again<br>
     */
    public void cleanMe() {
        participatedPlayers.clear();
        participatedPlayers = new HashMap<>();
        points = 0;
    }

    public void onEventNotStarted() {
        for (PlayerInstance player : participatedPlayers.values()) {
            if (player != null) {
                player.setEvent(null);
            }
        }
        cleanMe();
    }

    /**
     * Is given player in this team?<br><br>
     *
     * @return boolean: true if player is in this team, otherwise false<br>
     */
    public boolean containsPlayer(int playerObjectId) {
        boolean containsPlayer;

        synchronized (participatedPlayers) {
            containsPlayer = participatedPlayers.containsKey(playerObjectId);
        }

        return containsPlayer;
    }

    /**
     * Returns the name of the team<br><br>
     *
     * @return String: name of the team<br>
     */
    public String getName() {
        return name;
    }

    /**
     * Returns the coordinates of the team spot<br><br>
     *
     * @return int[]: team coordinates<br>
     */
    public Point3D getCoords() {
        return coordinates;
    }

    /**
     * Returns the points of the team<br><br>
     *
     * @return short: team points<br>
     */
    public short getPoints() {
        return points;
    }

    /**
     * Returns name and instance of all participated players in HashMap<br><br>
     *
     * @return Map<String   ,       Player>: map of players in this team<br>
     */
    public Map<Integer, PlayerInstance> getParticipatedPlayers() {
        // Map<Integer, PlayerInstance> participatedPlayers = null;

        synchronized (participatedPlayers) {
            // this.participatedPlayers = participatedPlayers;
        }

        return participatedPlayers;
    }

    /**
     * Returns player count of this team<br><br>
     *
     * @return int: number of players in team<br>
     */
    public int getParticipatedPlayerCount() {
        int participatedPlayerCount;

        synchronized (participatedPlayers) {
            participatedPlayerCount = participatedPlayers.size();
        }

        return participatedPlayerCount;
    }

    public int getAlivePlayerCount() {
        int alivePlayerCount = 0;

        ArrayList<PlayerInstance> toIterate = new ArrayList<>(participatedPlayers.values());
        for (PlayerInstance player : toIterate) {
            if (!player.isOnline() || player.getClient() == null || player.getEvent() == null) {
                participatedPlayers.remove(player.getObjectId());
            }
            if (!player.isDead()) {
                alivePlayerCount++;
            }
        }
        return alivePlayerCount;
    }

    public int getHealersCount() {
        int count = 0;

        for (PlayerInstance player : participatedPlayers.values()) {
            if (player.getClassId().getId() == 146) {
                count++;
            }
        }
        return count;
    }

    public PlayerInstance selectRandomParticipant() {
        int rnd = Rnd.get(participatedPlayers.size());
        int i = 0;
        for (PlayerInstance participant : participatedPlayers.values()) {
            if (i == rnd) {
                return participant;
            }
            i++;
        }
        return null;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setCoords(Point3D coords) {
        coordinates = coords;
    }

    public Spawn getFlagSpawn() {
        return flagSpawn;
    }

    public void setFlagSpawn(Spawn spawn) {
        if (flagSpawn != null && flagSpawn.getLastSpawn() != null) {
            flagSpawn.getLastSpawn().deleteMe();
            flagSpawn.stopRespawn();
            SpawnTable.getInstance().deleteSpawn(flagSpawn, false);
        }

        flagSpawn = spawn;
    }

    public int getFlagId() {
        return flagId;
    }

    public void setVIP(PlayerInstance character) {
        VIP = character;
    }

    public PlayerInstance getVIP() {
        return VIP;
    }

    public boolean isAlive() {
        return getAlivePlayerCount() > 0;
    }

    public Spawn getGolemSpawn() {
        return golemSpawn;
    }

    public void setGolemSpawn(Spawn spawn) {
        if (golemSpawn != null && golemSpawn.getLastSpawn() != null) {
            golemSpawn.getLastSpawn().deleteMe();
        }
        golemSpawn = spawn;
    }

    public int getGolemId() {
        return golemId;
    }
}
<file_sep>/java/org/l2jmobius/gameserver/model/actor/instance/JukeBoxInstance.java
/*
 * This file is part of the L2J Mobius project.
 *
 * 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 org.l2jmobius.gameserver.model.actor.instance;

import org.l2jmobius.gameserver.data.xml.impl.RecipeData;
import org.l2jmobius.gameserver.enums.InstanceType;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
import org.l2jmobius.gameserver.model.holders.RecipeHolder;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.*;
import org.l2jmobius.gameserver.util.BuilderUtil;

/**
 * @author Inia
 */
public class JukeBoxInstance extends Npc {

    public JukeBoxInstance(NpcTemplate template) {
        super(template);
        setRandomAnimation(false);
        setInstanceType(InstanceType.JukeBoxInstance);
    }


    private void playSound(PlayerInstance activeChar, String sound) {
        if (activeChar.getInventory().getInventoryItemCount(5343, 0) > 0) {
			activeChar.destroyItemByItemId("jukebox", 5343, 1, null, true);
            final PlaySound snd = new PlaySound(1, sound, 0, 0, 0, 0, 0);
            activeChar.sendPacket(snd);
            activeChar.broadcastPacket(snd);
            BuilderUtil.sendSysMessage(activeChar, "Playing " + sound + ".");
            broadcastPacket(new SocialAction(this.getObjectId(), 1));
        } else {
            activeChar.sendMessage("You need a music ticket !");
        }

    }

    @Override
    public void onBypassFeedback(PlayerInstance player, String command) {
        if (player == null) {
            return;
        }

        if (command.startsWith("play")) {
            String[] cmds = command.split(" ");
            playSound(player, cmds[1]);
            return;

        }
        if (command.startsWith("Chat")) {
            if (command.substring(5) == null) {
                showChatWindow(player);
                return;
            }
            showChatWindow(player, Integer.valueOf(command.substring(5)));
        } else {
            super.onBypassFeedback(player, command);
        }
    }


    @Override
    public String getHtmlPath(int npcId, int value, PlayerInstance player) {
        String pom;
        if (value == 0) {
            pom = Integer.toString(npcId);
        } else {
            pom = npcId + "-" + value;
        }
        return "data/html/jukebox/" + pom + ".htm";
    }

    @Override
    public void showChatWindow(PlayerInstance player, int val) {
        NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
        html.setFile(player, getHtmlPath(getId(), val, player));
        html.replace("%objectId%", String.valueOf(getObjectId()));
        player.sendPacket(html);
    }

}
<file_sep>/java/org/l2jmobius/commons/util/xml/XmlDocument.kt
package l2server.util.xml

import org.w3c.dom.Node
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.InputStream
import javax.xml.parsers.DocumentBuilderFactory

/**
 * @author Pere
 */
class XmlDocument {

	val root: XmlNode

	constructor(file: File) {
		if (!file.exists()) {
			throw FileNotFoundException("The following XML could not be loaded: " + file.absolutePath)
		}

		val stream = FileInputStream(file)
		try {
			root = load(stream)
		} catch (e: Exception) {
			throw RuntimeException("Failed to load XML file $file", e)
		}
	}

	constructor(stream: InputStream) {
		root = load(stream)
	}

	fun getChildren(): List<XmlNode> = root.getChildren()

	private fun load(stream: InputStream): XmlNode {
		val doc = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder().parse(stream)

		var baseNode: Node? = doc.firstChild
		while (baseNode != null) {
			if (baseNode.nodeType == Node.ELEMENT_NODE) {
				return XmlNode(baseNode)
			}
			baseNode = baseNode.nextSibling
		}

		throw RuntimeException("Tried to load an empty XML document!")
	}

	companion object {
		private val DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance()

		init {
			DOCUMENT_BUILDER_FACTORY.isValidating = false
			DOCUMENT_BUILDER_FACTORY.isIgnoringComments = true
		}
	}
}
<file_sep>/config/Custom/ScreenWelcomeMessage.ini
# ---------------------------------------------------------------------------
# Welcome message
# ---------------------------------------------------------------------------

# Show screen welcome message on character login
# Default: False
ScreenWelcomeMessageEnable = True

# Screen welcome message text to show on character login if enabled
# ('#' for a new line, but message can have max 2 lines)
ScreenWelcomeMessageText = Join the discord for any question

# Show screen welcome message for x seconds when character log in to game if enabled
ScreenWelcomeMessageTime = 20
<file_sep>/java/org/l2jmobius/gameserver/events/instanced/EventPrize.java
package org.l2jmobius.gameserver.events.instanced;

import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.util.xml.XmlNode;

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

public class EventPrize {
    protected final float chance;
    protected final boolean dependsOnPerformance;

    public EventPrize(XmlNode node) {
        chance = node.getFloat("chance");
        dependsOnPerformance = node.getBool("dependsOnPerformance", false);
    }

    public EventPrizeItem getItem() {
        return null;
    }

    public static class EventPrizeItem extends EventPrize {
        private final int id;
        private final int min;
        private final int max;

        public EventPrizeItem(XmlNode node) {
            super(node);
            id = node.getInt("id");
            min = node.getInt("min");
            max = node.getInt("max");
        }

        public int getId() {
            return id;
        }

        public int getMin() {
            return min;
        }

        public int getMax() {
            return max;
        }

        @Override
        public EventPrizeItem getItem() {
            return this;
        }
    }

    public static class EventPrizeCategory extends EventPrize {
        private final List<EventPrizeItem> items = new ArrayList<>();

        public EventPrizeCategory(XmlNode node) {
            super(node);
            for (XmlNode subNode : node.getChildren()) {
                if (subNode.getName().equalsIgnoreCase("item")) {
                    items.add(new EventPrizeItem(subNode));
                }
            }
        }

        @Override
        public EventPrizeItem getItem() {
            float rnd = Rnd.get(100000) / 1000.0f;
            float percent = 0.0f;
            for (EventPrizeItem item : items) {
                percent += item.getChance();
                if (percent > rnd) {
                    return item;
                }
            }

            return items.get(0);
        }
    }

    public float getChance() {
        return chance;
    }

    public boolean dependsOnPerformance() {
        return dependsOnPerformance;
    }
}
<file_sep>/java/org/l2jmobius/gameserver/gui/ServerGui.java
package org.l2jmobius.gameserver.gui;

import javax.swing.*;
import java.awt.*;

public class ServerGui {
    private static JFrame frame;

    //private JMenuBar menuBar = new JMenuBar();

    //private JMenu fileMenu = new JMenu("File");
    //private JMenu helpMenu = new JMenu("Help");

    //private ActionListener menuListener = new MenuActionListener();

    private static JTabbedPane tabPane = new JTabbedPane();

    private static ConsoleTab consoleTab;

    private static AdminTab adminTab;

    private static CommandTab commandTab;

    public void init() {
        frame = new JFrame("L2 Server");

        //Menu Bar Items
        //File Menu
		/*JMenuItem itemExit = new JMenuItem("Exit");
        itemExit.setActionCommand("Exit");
		itemExit.addActionListener(menuListener);
		fileMenu.add(itemExit);
		//Help
		JMenuItem itemAbout = new JMenuItem("About");
		itemAbout.setActionCommand("About");
		itemAbout.addActionListener(menuListener);
		helpMenu.add(itemAbout);
		menuBar.add(fileMenu);
		menuBar.add(helpMenu);
		frame.setJMenuBar(menuBar);*/

        // Console Tab
        consoleTab = new ConsoleTab(true);
        adminTab = new AdminTab();
        commandTab = new CommandTab();

        frame.setLayout(new BorderLayout());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        tabPane.add("Console", consoleTab);
        tabPane.add("Admin", adminTab);
        tabPane.add("Command", commandTab);

        //build the frame
        frame.add(tabPane, BorderLayout.CENTER);

        //add the window listeners
        addListeners();

        frame.setLocation(50, 50);
        frame.setMinimumSize(new Dimension(930, 700));
        //frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setVisible(true);
    }

    public JTabbedPane getTabPane() {
        return tabPane;
    }

    private void addListeners() {
        //Window Closing
        /*frame.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent event)
			{
				close();
			}
		});*/
    }

    // MenuActions
	/*public class MenuActionListener implements ActionListener
	{
		public void actionPerformed(ActionEvent ev)
		{
			String actionCmd = ev.getActionCommand();
			if (actionCmd.equals("Exit"))
			{
				System.exit(0);
			}
		}
	}*/

    public static JFrame getMainFrame() {
        return frame;
    }

    public static ConsoleTab getConsoleTab() {
        return consoleTab;
    }

    public static AdminTab getAdminTab() {
        return adminTab;
    }
}
<file_sep>/java/org/l2jmobius/gameserver/events/instanced/EventConfig.java
package org.l2jmobius.gameserver.events.instanced;

import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.events.instanced.EventInstance.EventType;
import org.l2jmobius.gameserver.events.instanced.types.*;

import java.util.Random;

public class EventConfig {
    private EventType type = EventType.TVT;
    private EventLocation location = null;
    private String[] teamNames = new String[4];

    public EventConfig() {
        selectEvent();

        if (location.getTeamCount() == 4) {
            while (selectTeamNames(Rnd.get(4))) {
            }
        } else {
            while (!selectTeamNames(Rnd.get(4))) {
            }
        }
    }

    public EventConfig(boolean pvp) {
        selectEvent();
        while (pvp != isPvp()) {
            selectEvent();
        }

        if (location.getTeamCount() == 4) {
            while (selectTeamNames(Rnd.get(4))) {
            }
        } else {
            while (!selectTeamNames(Rnd.get(4))) {
            }
        }
    }

    public boolean isType(EventType type) {
        return this.type == type;
    }

    public EventType getType() {
        return type;
    }

    public boolean isAllVsAll() {
        return type == EventType.Survival || type == EventType.DeathMatch || type == EventType.KingOfTheHill || type == EventType.CursedBattle ||
                type == EventType.StalkedSalkers || type == EventType.SimonSays;
    }

    public boolean needsClosedArena() {
        return type == EventType.CaptureTheFlag || type == EventType.VIP || type == EventType.Survival || type == EventType.TeamSurvival ||
                type == EventType.CursedBattle || type == EventType.StalkedSalkers;
    }

    public boolean spawnsPlayersRandomly() {
        return type == EventType.Survival || type == EventType.DeathMatch || type == EventType.CursedBattle || type == EventType.StalkedSalkers ||
                type == EventType.SimonSays;
    }

    public boolean needsRandomCoords() {
        return spawnsPlayersRandomly() || type == EventType.LuckyChests;
    }

    public boolean hasNoLevelLimits() {
        return type == EventType.LuckyChests || type == EventType.StalkedSalkers || type == EventType.SimonSays;
    }

    public boolean isPvp() {
        return type == EventType.TVT || type == EventType.CaptureTheFlag || type == EventType.VIP || type == EventType.Survival ||
                type == EventType.DeathMatch || type == EventType.TeamSurvival || type == EventType.CursedBattle;
    }

    public String getTeamName(int id) {
        return teamNames[id];
    }

    public boolean selectTeamNames(int name) {
        boolean dual = false;
        switch (name) {
            case 0:
                teamNames[0] = "Blue";
                teamNames[1] = "Red";
                dual = true;
                break;
            case 1:
                teamNames[0] = "Water";
                teamNames[1] = "Fire";
                teamNames[2] = "Earth";
                teamNames[3] = "Wind";
                break;
            case 2:
                teamNames[0] = "Winter";
                teamNames[1] = "Autumn";
                teamNames[2] = "Summer";
                teamNames[3] = "Spring";
                break;
            case 3:
                teamNames[0] = "Blue";
                teamNames[1] = "Red";
                teamNames[2] = "Yellow";
                teamNames[3] = "Green";
        }

        return dual;
    }

    private void selectEvent() {
        double[] chances = new double[]{
                40.0, // TvT
                40.0, // Capture the Flag
                20.0, // Lucky Chests
                0.0 // Simon Says
                /*
                                                            5.0, // VIP TvT
                                                            9.0, // Survival
                                                            11.0, // Death Match
                                                            2.0, // King of the Hill
                                                            18.0, // Team Survival
                                                            0.0, // Cursed Battle
                                                            5.0, // Destroy the Golem
                                                            2.0, // Field Domination
                                                            2.0, // Stalked Stalkers
                                                            */
        };

        double total = 0;
        for (double chance : chances) {
            total += chance;
        }

        double type = Rnd.nextDouble() * total;
        for (int i = 0; i < chances.length; i++) {
            type -= chances[i];
            if (type < 0.0) {
                this.type = EventType.values()[i];
                break;
            }
        }

        selectLocation();
    }

    public int randomBetween(int low, int high) {
        Random r = new Random();
        return r.nextInt(high-low) + low;
    }


    public EventLocation getLocation() {
        return location;
    }

    public void setLocation(EventLocation location) {
        this.location = location;
    }

    public void selectLocation() {
        location = EventsManager.getInstance().getRandomLocation();

        if (needsClosedArena() || needsRandomCoords()) {
            while (location.getZone() == null) {
                location = EventsManager.getInstance().getRandomLocation();
            }
        } else if (isType(EventType.KingOfTheHill)) {
            while (!location.isHill()) {
                location = EventsManager.getInstance().getRandomLocation();
            }
        }
    }

    public int getMaxTeamPlayers() {
        return isAllVsAll() ? location.getMaxPlayers() : location.getMaxTeamPlayers();
    }

    public int getMinPlayers() {
        return isAllVsAll() ? 2 : location.getTeamCount();
    }

    public EventInstance createInstance(int id) {
        switch (type) {
            case TVT:
                return new TeamVsTeam(id, this);
            case LuckyChests:
                return new LuckyChests(id, this);
            case CaptureTheFlag:
                return new CaptureTheFlag(id, this);
            case SimonSays:
                return new SimonSays(id, this);
                /*

            case VIP:
                return new VIPTeamVsTeam(id, this);
            case DeathMatch:
                return new DeathMatch(id, this);
            case Survival:
                return new Survival(id, this);
            case KingOfTheHill:
                return new KingOfTheHill(id, this);

            case TeamSurvival:
                return new TeamSurvival(id, this);
            case CursedBattle:
                return new CursedBattle(id, this);
            case DestroyTheGolem:
                return new DestroyTheGolem(id, this);
            case FieldDomination:
                return new FieldDomination(id, this);
            case StalkedSalkers:
                return new StalkedStalkers(id, this);
             */
        }

        return new TeamVsTeam(id, this);
    }

    public String getEventName() {
        if (location == null) {
            return "No event";
        }

        switch (type) {
            case CaptureTheFlag:
                if (location.getTeamCount() == 4) {
                    return "Four teams Capture the Flag";
                }

                return "Capture the Flag";

            case VIP:
                if (location.getTeamCount() == 4) {
                    return "VIP TvTvTvT";
                }

                return "VIP TvT";

            case Survival:
                return "Survival";

            case DeathMatch:
                return "Death Match";

            case LuckyChests:
                if (location.getTeamCount() == 4) {
                    return "Four teams Lucky Chests";
                }

                return "Lucky Chests";

            case KingOfTheHill:
                return "King of The Hill";

            case TeamSurvival:
                if (location.getTeamCount() == 4) {
                    return "Team Survival";
                }

                return "Team Survival";

            case CursedBattle:
                return "Cursed Battle";

            case DestroyTheGolem:
                if (location.getTeamCount() == 4) {
                    return "Four teams Destroy the Golem";
                }

                return "Destroy the Golem";

            case FieldDomination:
                if (location.getTeamCount() == 4) {
                    return "Four teams Field Domination";
                }

                return "Field Domination";

            case StalkedSalkers:
                return "Stalked Stalkers";

            case SimonSays:
                return "Simon Says";

            default:
                if (location.getTeamCount() == 4) {
                    return "TvTvTvT";
                }

                return "Team vs Team";
        }
    }

    public String getEventLocationName() {
        if (location == null) {
            return "No event";
        }

        return location.getName();
    }

    public int getEventImageId() {
        switch (type) {
            case TVT:
                return 20012;
            case CaptureTheFlag:
                return 20002;
            case VIP:
                return 20013;
            case Survival:
                return 20004;
            case DeathMatch:
                return 20009;
            case KingOfTheHill:
                return 20008;
            case LuckyChests:
                return 20001;
            case TeamSurvival:
                return 20005;
            case CursedBattle:
                return 20003;
            case DestroyTheGolem:
                return 20007;
            case FieldDomination:
                return 20006;
            case StalkedSalkers:
                return 20010;
            case SimonSays:
                return 20011;
        }

        return 0;
    }

    public String getEventString() {
        if (location == null) {
            return "No event";
        }

        String eventString;
        switch (type) {
            case CaptureTheFlag:
                eventString = "Capture the Flag";
                if (location.getTeamCount() == 4) {
                    eventString = "Four teams Capture the Flag";
                }
                break;
            case VIP:
                eventString = "VIP TvT";
                if (location.getTeamCount() == 4) {
                    eventString = "VIP TvTvTvT";
                }
                break;
            case Survival:
                eventString = "Survival";
                break;
            case DeathMatch:
                eventString = "Death Match";
                break;
            case LuckyChests:
                eventString = "Lucky Chests";
                if (location.getTeamCount() == 4) {
                    eventString = "Four teams Lucky Chests";
                }
                break;
            case KingOfTheHill:
                eventString = "King of The Hill";
                break;
            case TeamSurvival:
                eventString = "Team Survival";
                if (location.getTeamCount() == 4) {
                    eventString = "Four Teams Survival";
                }
                break;
            case CursedBattle:
                eventString = "Cursed Battle";
                break;
            case DestroyTheGolem:
                eventString = "Destroy the Golem";
                if (location.getTeamCount() == 4) {
                    eventString = "Four teams Destroy the Golem";
                }
                break;
            case FieldDomination:
                eventString = "Field Domination";
                if (location.getTeamCount() == 4) {
                    eventString = "Four teams Field Domination";
                }
                break;
            case StalkedSalkers:
                eventString = "Stalked Stalkers";
                break;
            case SimonSays:
                eventString = "Simon Says";
                break;
            default:
                eventString = "Team vs Team";
                if (location.getTeamCount() == 4) {
                    eventString = "TvTvTvT";
                }
        }

        eventString += " at " + location.getName();

        return eventString;
    }
}
<file_sep>/data/scripts/handlers/itemhandlers/Deck.java
/*
 * This file is part of the L2J Mobius project.
 * 
 * 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 scripts.handlers.itemhandlers;

import org.l2jmobius.Config;
import org.l2jmobius.gameserver.cache.HtmCache;
import org.l2jmobius.gameserver.datatables.ItemTable;
import org.l2jmobius.gameserver.enums.DropType;
import org.l2jmobius.gameserver.handler.IItemHandler;
import org.l2jmobius.gameserver.model.actor.Npc;
import org.l2jmobius.gameserver.model.actor.Playable;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.model.holders.DropHolder;
import org.l2jmobius.gameserver.model.items.Item;
import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
import org.l2jmobius.gameserver.model.stats.Stat;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import org.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
import org.l2jmobius.gameserver.util.Util;

import java.text.DecimalFormat;
import java.util.List;

public class Deck implements IItemHandler
{

	private static final int DROP_LIST_ITEMS_PER_PAGE = 10;

	@Override
	public boolean useItem(Playable playable, ItemInstance item, boolean forceUse)
	{
		if (!(playable instanceof PlayerInstance))
		{
			return false;
		}

		PlayerInstance player = (PlayerInstance) playable;
		Object target = player.getTarget();

		if (target == null || !(target instanceof Npc))
		{
			player.sendMessage("You should target a monster to see its drop list!");
			return false;
		}

		Npc mob = (Npc) player.getTarget();
		if (mob != null)
		{
			sendNpcDropList(player, mob, DropType.DROP, 1);
		}
		return true;
	}

	private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int page)
	{
		final List<DropHolder> dropList = npc.getTemplate().getDropList(dropType);
		if (dropList == null)
		{
			return;
		}

		int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE;
		if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size())
		{
			pages++;
		}

		final StringBuilder pagesSb = new StringBuilder();
		if (pages > 1)
		{
			pagesSb.append("<table><tr>");
			for (int i = 0; i < pages; i++)
			{
				pagesSb.append("<td align=center><button value=\"" + (i + 1) + "\" width=20 height=20 action=\"bypass NpcViewMod dropList " + dropType + " " + npc.getObjectId() + " " + i + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>");
			}
			pagesSb.append("</tr></table>");
		}

		if (page >= pages)
		{
			page = pages - 1;
		}

		final int start = page > 0 ? page * DROP_LIST_ITEMS_PER_PAGE : 0;
		int end = (page * DROP_LIST_ITEMS_PER_PAGE) + DROP_LIST_ITEMS_PER_PAGE;
		if (end > dropList.size())
		{
			end = dropList.size();
		}

		final DecimalFormat amountFormat = new DecimalFormat("#,###");
		final DecimalFormat chanceFormat = new DecimalFormat("0.00##");
		int leftHeight = 0;
		int rightHeight = 0;
		final double dropAmountEffectBonus = player.getStat().getValue(Stat.BONUS_DROP_AMOUNT, 1);
		final double dropRateEffectBonus = player.getStat().getValue(Stat.BONUS_DROP_RATE, 1);
		final double spoilRateEffectBonus = player.getStat().getValue(Stat.BONUS_SPOIL_RATE, 1);
		final StringBuilder leftSb = new StringBuilder();
		final StringBuilder rightSb = new StringBuilder();
		String limitReachedMsg = "";
		for (int i = start; i < end; i++)
		{
			final StringBuilder sb = new StringBuilder();
			final int height = 64;
			final DropHolder dropItem = dropList.get(i);
			final Item item = ItemTable.getInstance().getTemplate(dropItem.getItemId());

			// real time server rate calculations
			double rateChance = 1;
			double rateAmount = 1;
			if (dropType == DropType.SPOIL)
			{
				rateChance = Config.RATE_SPOIL_DROP_CHANCE_MULTIPLIER;
				rateAmount = Config.RATE_SPOIL_DROP_AMOUNT_MULTIPLIER;

				// also check premium rates if available
				if (Config.PREMIUM_SYSTEM_ENABLED && player.hasPremiumStatus())
				{
					rateChance *= Config.PREMIUM_RATE_SPOIL_CHANCE;
					rateAmount *= Config.PREMIUM_RATE_SPOIL_AMOUNT;
				}

				// bonus spoil rate effect
				rateChance *= spoilRateEffectBonus;
			}
			else
			{
				if (Config.RATE_DROP_CHANCE_BY_ID.get(dropItem.getItemId()) != null)
				{
					rateChance *= Config.RATE_DROP_CHANCE_BY_ID.get(dropItem.getItemId());
				}
				else if (item.hasExImmediateEffect())
				{
					rateChance *= Config.RATE_HERB_DROP_CHANCE_MULTIPLIER;
				}
				else if (npc.isRaid())
				{
					rateChance *= Config.RATE_RAID_DROP_CHANCE_MULTIPLIER;
				}
				else
				{
					rateChance *= Config.RATE_DEATH_DROP_CHANCE_MULTIPLIER;
				}

				if (Config.RATE_DROP_AMOUNT_BY_ID.get(dropItem.getItemId()) != null)
				{
					rateAmount *= Config.RATE_DROP_AMOUNT_BY_ID.get(dropItem.getItemId());
				}
				else if (item.hasExImmediateEffect())
				{
					rateAmount *= Config.RATE_HERB_DROP_AMOUNT_MULTIPLIER;
				}
				else if (npc.isRaid())
				{
					rateAmount *= Config.RATE_RAID_DROP_AMOUNT_MULTIPLIER;
				}
				else
				{
					rateAmount *= Config.RATE_DEATH_DROP_AMOUNT_MULTIPLIER;
				}

				// also check premium rates if available
				if (Config.PREMIUM_SYSTEM_ENABLED && player.hasPremiumStatus())
				{
					if (Config.PREMIUM_RATE_DROP_CHANCE_BY_ID.get(dropItem.getItemId()) != null)
					{
						rateChance *= Config.PREMIUM_RATE_DROP_CHANCE_BY_ID.get(dropItem.getItemId());
					}
					else if (item.hasExImmediateEffect())
					{
						// TODO: Premium herb chance? :)
					}
					else if (npc.isRaid())
					{
						// TODO: Premium raid chance? :)
					}
					else
					{
						rateChance *= Config.PREMIUM_RATE_DROP_CHANCE;
					}

					if (Config.PREMIUM_RATE_DROP_AMOUNT_BY_ID.get(dropItem.getItemId()) != null)
					{
						rateAmount *= Config.PREMIUM_RATE_DROP_AMOUNT_BY_ID.get(dropItem.getItemId());
					}
					else if (item.hasExImmediateEffect())
					{
						// TODO: Premium herb amount? :)
					}
					else if (npc.isRaid())
					{
						// TODO: Premium raid amount? :)
					}
					else
					{
						rateAmount *= Config.PREMIUM_RATE_DROP_AMOUNT;
					}
				}

				// bonus drop amount effect
				rateAmount *= dropAmountEffectBonus;
				// bonus drop rate effect
				rateChance *= dropRateEffectBonus;
			}

			sb.append("<table width=332 cellpadding=2 cellspacing=0 background=\"L2UI_CT1.Windows.Windows_DF_TooltipBG\">");
			sb.append("<tr><td width=32 valign=top>");
			sb.append("<button width=\"32\" height=\"32\" back=\"" + (item.getIcon() == null ? "icon.etc_question_mark_i00" : item.getIcon()) + "\" fore=\"" + (item.getIcon() == null ? "icon.etc_question_mark_i00" : item.getIcon()) + "\" itemtooltip=\"" + dropItem.getItemId() + "\">");
			sb.append("</td><td fixwidth=300 align=center><font name=\"hs9\" color=\"CD9000\">");
			sb.append(item.getName());
			sb.append("</font></td></tr><tr><td width=32></td><td width=300><table width=295 cellpadding=0 cellspacing=0>");
			sb.append("<tr><td width=48 align=right valign=top><font color=\"LEVEL\">Amount:</font></td>");
			sb.append("<td width=247 align=center>");

			final long min = (long) (dropItem.getMin() * rateAmount);
			final long max = (long) (dropItem.getMax() * rateAmount);
			if (min == max)
			{
				sb.append(amountFormat.format(min));
			}
			else
			{
				sb.append(amountFormat.format(min));
				sb.append(" - ");
				sb.append(amountFormat.format(max));
			}

			sb.append("</td></tr><tr><td width=48 align=right valign=top><font color=\"LEVEL\">Chance:</font></td>");
			sb.append("<td width=247 align=center>");
			sb.append(chanceFormat.format(Math.min(dropItem.getChance() * rateChance, 100)));
			sb.append("%</td></tr></table></td></tr><tr><td width=32></td><td width=300>&nbsp;</td></tr></table>");
			if ((sb.length() + rightSb.length() + leftSb.length()) < 16000) // limit of 32766?
			{
				if (leftHeight >= (rightHeight + height))
				{
					rightSb.append(sb);
					rightHeight += height;
				}
				else
				{
					leftSb.append(sb);
					leftHeight += height;
				}
			}
			else
			{
				limitReachedMsg = "<br><center>Too many drops! Could not display them all!</center>";
			}
		}

		final StringBuilder bodySb = new StringBuilder();
		bodySb.append("<table><tr>");
		bodySb.append("<td>");
		bodySb.append(leftSb.toString());
		bodySb.append("</td><td>");
		bodySb.append(rightSb.toString());
		bodySb.append("</td>");
		bodySb.append("</tr></table>");

		String html = HtmCache.getInstance().getHtm(player, "data/html/mods/NpcView/DropList.htm");
		if (html == null)
		{
			LOGGER.warning(handlers.bypasshandlers.NpcViewMod.class.getSimpleName() + ": The html file data/html/mods/NpcView/DropList.htm could not be found.");
			return;
		}
		html = html.replace("%name%", npc.getName());
		html = html.replace("%dropListButtons%", getDropListButtons(npc));
		html = html.replace("%pages%", pagesSb.toString());
		html = html.replace("%items%", bodySb.toString() + limitReachedMsg);
		Util.sendCBHtml(player, html);
	}


	private static String getDropListButtons(Npc npc)
	{
		final StringBuilder sb = new StringBuilder();
		final List<DropHolder> dropListDeath = npc.getTemplate().getDropList(DropType.DROP);
		final List<DropHolder> dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL);
		if ((dropListDeath != null) || (dropListSpoil != null))
		{
			sb.append("<table width=275 cellpadding=0 cellspacing=0><tr>");
			if (dropListDeath != null)
			{
				sb.append("<td align=center><button value=\"Show Drop\" width=100 height=25 action=\"bypass NpcViewMod dropList DROP " + npc.getObjectId() + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>");
			}

			if (dropListSpoil != null)
			{
				sb.append("<td align=center><button value=\"Show Spoil\" width=100 height=25 action=\"bypass NpcViewMod dropList SPOIL " + npc.getObjectId() + "\" back=\"L2UI_CT1.Button_DF_Calculator_Down\" fore=\"L2UI_CT1.Button_DF_Calculator\"></td>");
			}

			sb.append("</tr></table>");
		}
		return sb.toString();
	}
}
