### Eclipse Workspace Patch 1.0
#P L2J_Server
Index: java/com/l2jserver/gameserver/model/actor/instance/L2TIMonsterInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2TIMonsterInstance.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2TIMonsterInstance.java	(working copy)
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.instance;
+
+import java.util.concurrent.ScheduledFuture;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.ThreadPoolManager;
+import com.l2jserver.gameserver.ai.CtrlIntention;
+import com.l2jserver.gameserver.model.L2CharPosition;
+import com.l2jserver.gameserver.model.Location;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
+import com.l2jserver.gameserver.model.entity.TownInvasion.TownInvasionEvent;
+import com.l2jserver.util.Rnd;
+
+/**
+ * Town invasion monster class that extends L2MonsterInstance. <br>
+ * <br>
+ * It declares point where monster is moving to and checks for players in aggresion radius. Attacks nearest target or move toward location. <br>
+ * <br>
+ * When target is found, its locked and saved as private parameter.
+ * @author Freedy
+ */
+public class L2TIMonsterInstance extends L2MonsterInstance
+{
+	// protected, because it's called from outside class inside this file
+	protected final Location loc;
+	protected ScheduledFuture<?> _movementTask = null;
+	
+	private L2Character target;
+	
+	/**
+	 * @param objectId
+	 * @param template
+	 */
+	public L2TIMonsterInstance(int objectId, L2NpcTemplate template)
+	{
+		super(objectId, template);
+		
+		setInstanceType(InstanceType.L2TIInstance);
+		target = null;
+		setLethalable(false);
+		setSeeThroughSilentMove(true);
+		setCanReturnToSpawnPoint(false);
+		setAutoAttackable(true);
+		if (TownInvasionEvent.getInstance().isWaving() || TownInvasionEvent.getInstance().isLast())
+		{
+			loc = TownInvasionEvent.getInstance().getCurrentTownLocation();
+		}
+		else
+		{
+			loc = null;
+		}
+	}
+	
+	@Override
+	public void onSpawn()
+	{
+		super.onSpawn();
+		// setRunning() can be commented if we want to use slower NPCs speed and make them just walk
+		setRunning();
+		if (loc != null)
+		{
+			startMaintanceTask();
+		}
+		else
+		{
+			// if there is not location defined, then there is no point to have monster here
+			deleteMe();
+		}
+	}
+	
+	@Override
+	public boolean doDie(L2Character killer)
+	{
+		if (!super.doDie(killer))
+		{
+			return false;
+		}
+		
+		if (_movementTask != null)
+		{
+			_movementTask.cancel(false);
+			_movementTask = null;
+		}
+		
+		giveReward(killer);
+		return true;
+	}
+	
+	@Override
+	public void deleteMe()
+	{
+		if (_movementTask != null)
+		{
+			_movementTask.cancel(true);
+			_movementTask = null;
+		}
+		super.deleteMe();
+	}
+	
+	/**
+	 * Rewards killer of NPC with configured prizes, for more info check towninvasion.properties file
+	 * @param killer - Killer of NPC
+	 */
+	private void giveReward(L2Character killer)
+	{
+		L2PcInstance player = (L2PcInstance) killer;
+		if (player == null)
+		{
+			return;
+		}
+		int item = 0;
+		int count = 0;
+		for (String s : Config.TI_MOB_KILL_REWARD)
+		{
+			if (s.split(",").length >= 2)
+			{
+				item = Integer.valueOf(s.split(",")[0]);
+				count = Integer.valueOf(s.split(",")[1]);
+				player.addItem("Town invasion", item, count, player, true);
+			}
+		}
+	}
+	
+	@Override
+	protected void startMaintanceTask()
+	{
+		super.startMaintanceTask();
+		
+		// schedule interval 200-500ms to check monster conditions
+		if (_movementTask == null)
+		{
+			_movementTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new Runnable()
+			{
+				@Override
+				public void run()
+				{
+					moveTowardLoc(loc);
+				}
+			}, Rnd.get(2000, 15000), Rnd.get(200, 500));
+		}
+	}
+	
+	// protected because its called from outside class in same file
+	protected void moveTowardLoc(final Location loc)
+	{
+		// if monster is in 150 range from king, it counts as hit to him, takes players life and unspawn
+		if (isInsideRadius(TownInvasionEvent.getInstance().getKing(), 150, false, false))
+		{
+			TownInvasionEvent.getInstance().takeLives();
+			deleteMe();
+			return;
+		}
+		
+		boolean insideRadius = false;
+		
+		// check if monster has target, if not check for chars in aggression radius and choose closest target
+		if (target == null)
+		{
+			target = (L2Character) getTarget();
+			double distance = 999999;
+			for (L2Character ch : getKnownList().getKnownCharactersInRadius(getAggroRange()))
+			{
+				if ((ch instanceof L2PcInstance))
+				{
+					if ((getDistanceSq(ch) < distance) && ch.isVisible() && !ch.isAlikeDead())
+					{
+						distance = getDistanceSq(ch);
+						target = ch;
+						insideRadius = true;
+					}
+				}
+			}
+		}
+		else
+		{
+			insideRadius = true;
+		}
+		
+		if (!insideRadius)
+		{
+			int x = loc.getX(), y = loc.getY(), z = loc.getZ();
+			x += getPosition().getX();
+			y += getPosition().getY();
+			z += getPosition().getZ();
+			x /= 2;
+			y /= 2;
+			z /= 2;
+			getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(x, y, z, 0));
+		}
+		else if ((target != null) && !target.isAlikeDead() && isInsideRadius(target, 2 * getAggroRange(), false, false))
+		{
+			setTarget(target);
+			if (isInsideRadius(target, getPhysicalAttackRange(), false, false))
+			{
+				if (!(getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK))
+				{
+					getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
+				}
+				doAttack(target);
+			}
+			else
+			{
+				getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(target.getPosition().getX(), target.getPosition().getY(), target.getPosition().getZ(), 0));
+			}
+		}
+		else
+		{
+			target = null;
+			setTarget(null);
+			moveTowardLoc(loc);
+		}
+	}
+}
Index: java/com/l2jserver/gameserver/model/L2Object.java
===================================================================
--- java/com/l2jserver/gameserver/model/L2Object.java	(revision 6478)
+++ java/com/l2jserver/gameserver/model/L2Object.java	(working copy)
@@ -174,7 +174,12 @@
 		L2NpcBufferInstance(L2Npc),
 		L2TvTEventNpcInstance(L2Npc),
 		L2WeddingManagerInstance(L2Npc),
-		L2EventMobInstance(L2Npc);
+		L2EventMobInstance(L2Npc),
+		
+		// Freedy
+		L2TIInstance(L2Attackable),
+		L2TIBossInstance(L2Attackable),
+		L2TIRegNpcInstance(L2Npc);
 		
 		private final InstanceType _parent;
 		private final long _typeL;
Index: java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionTown.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionTown.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionTown.java	(working copy)
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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.
+ * 
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity.TownInvasion;
+
+import com.l2jserver.gameserver.model.Location;
+
+/**
+ * A class holding data for one town, that can be attacked during town invasion event.
+ * @author Freedy
+ */
+public class TownInvasionTown
+{
+	private final String _townName;
+	private final int[] _spawnCoordinates;
+	private final Location _loc;
+	
+	/**
+	 * @param townName - Name of currently attacked town
+	 * @param spawnCoordinates - Points around which will be monsters spawned
+	 * @param loc - Point where are players teleported after event starts or ends and where are all monsters headed after spawn
+	 */
+	public TownInvasionTown(String townName, int[] spawnCoordinates, Location loc)
+	{
+		_townName = townName;
+		_spawnCoordinates = spawnCoordinates;
+		_loc = loc;
+	}
+	
+	/**
+	 * @return the _spawnCoordinates
+	 */
+	public int[] get_spawnCoordinates()
+	{
+		return _spawnCoordinates;
+	}
+	
+	/**
+	 * @return the _townName
+	 */
+	public String get_townName()
+	{
+		return _townName;
+	}
+	
+	/**
+	 * @return the _loc
+	 */
+	public Location get_loc()
+	{
+		return _loc;
+	}
+	
+}
Index: java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionTask.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionTask.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionTask.java	(working copy)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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.
+ * 
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity.TownInvasion;
+
+import java.util.concurrent.ScheduledFuture;
+import java.util.logging.Logger;
+
+import com.l2jserver.gameserver.ThreadPoolManager;
+
+/**
+ * A running task of town invasion. Decides what method should be called from TownInvasionEvent and when.
+ * @author Freedy
+ */
+public class TownInvasionTask implements Runnable
+{
+	protected static final Logger _log = Logger.getLogger(TownInvasionTask.class.getName());
+	
+	private long _startTime;
+	
+	// used to store only one task in thread pool manager
+	private ScheduledFuture<?> _task;
+	
+	public TownInvasionTask(long time)
+	{
+		_startTime = time;
+		_task = ThreadPoolManager.getInstance().scheduleGeneral(this, _startTime);
+	}
+	
+	@Override
+	public void run()
+	{
+		int delay = Math.round((_startTime - System.currentTimeMillis()));
+		
+		if ((delay < 0))
+		{
+			// start registration
+			if (TownInvasionEvent.getInstance().isLoaded() && TownInvasionEvent.getInstance().isInActive())
+			{
+				TownInvasionEvent.getInstance().startReg();
+			}
+			// start event
+			else if (TownInvasionEvent.getInstance().isRegistration())
+			{
+				TownInvasionEvent.getInstance().eventStart();
+			}
+			// spawn wave
+			else if (TownInvasionEvent.getInstance().isWaving() && TownInvasionEvent.getInstance().checkLives())
+			{
+				TownInvasionEvent.getInstance().spawnWave(false);
+			}
+			// spawn last wave (most likely boss)
+			else if (TownInvasionEvent.getInstance().isLast() && TownInvasionEvent.getInstance().checkLives())
+			{
+				TownInvasionEvent.getInstance().spawnWave(true);
+			}
+			// end event and schedule next start
+			else
+			{
+				TownInvasionEvent.getInstance().eventStop();
+			}
+		}
+		else
+		{
+			cancel();
+			_task = ThreadPoolManager.getInstance().scheduleGeneral(this, delay);
+		}
+	}
+	
+	public void setStartTime(long time)
+	{
+		_startTime = time;
+	}
+	
+	public void cancel()
+	{
+		_task.cancel(true);
+		_task = null;
+	}
+}
Index: build.xml
===================================================================
--- build.xml	(revision 6478)
+++ build.xml	(working copy)
@@ -47,7 +47,7 @@
 		</mapper>
 	</pathconvert>
 
-	<target name="init" depends="checkRequirements,getChangelogDateVersion" description="Create the output directories.">
+	<target name="init" description="Create the output directories.">
 		<delete dir="${build.bin}" quiet="true" />
 		<mkdir dir="${build.bin}" />
 	</target>
Index: java/com/l2jserver/gameserver/GameServer.java
===================================================================
--- java/com/l2jserver/gameserver/GameServer.java	(revision 6478)
+++ java/com/l2jserver/gameserver/GameServer.java	(working copy)
@@ -120,6 +120,7 @@
 import com.l2jserver.gameserver.model.PartyMatchWaitingList;
 import com.l2jserver.gameserver.model.entity.Hero;
 import com.l2jserver.gameserver.model.entity.TvTManager;
+import com.l2jserver.gameserver.model.entity.TownInvasion.TownInvasionManager;
 import com.l2jserver.gameserver.model.olympiad.Olympiad;
 import com.l2jserver.gameserver.network.L2GameClient;
 import com.l2jserver.gameserver.network.L2GamePacketHandler;
@@ -357,6 +358,8 @@
 		
 		TaskManager.getInstance();
 		
+		TownInvasionManager.getInstance();
+		
 		AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID);
 		
 		if (Config.ALLOW_MAIL)
Index: java/com/l2jserver/gameserver/model/actor/instance/L2MonsterInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2MonsterInstance.java	(revision 6478)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2MonsterInstance.java	(working copy)
@@ -113,7 +113,7 @@
 				getMinionList().onMasterSpawn();
 			}
 			
-			startMaintenanceTask();
+			startMaintanceTask();
 		}
 		
 		// dynamic script-based minions spawned here, after all preparations.
@@ -139,7 +139,7 @@
 	/**
 	 * Spawn all minions at a regular interval
 	 */
-	protected void startMaintenanceTask()
+	protected void startMaintanceTask()
 	{
 		// maintenance task now used only for minions spawn
 		if (getTemplate().getMinionData() == null)
Index: java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java	(revision 6478)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java	(working copy)
@@ -171,6 +171,7 @@
 import com.l2jserver.gameserver.model.entity.L2Event;
 import com.l2jserver.gameserver.model.entity.Siege;
 import com.l2jserver.gameserver.model.entity.TvTEvent;
+import com.l2jserver.gameserver.model.entity.TownInvasion.TownInvasionEvent;
 import com.l2jserver.gameserver.model.fishing.L2Fish;
 import com.l2jserver.gameserver.model.fishing.L2Fishing;
 import com.l2jserver.gameserver.model.holders.ItemHolder;
@@ -469,6 +470,10 @@
 	/** The PvP Flag state of the L2PcInstance (0=White, 1=Purple) */
 	private byte _pvpFlag;
 	
+	// XXX: Freedy
+	/** Town Invasion state of player - signed=true, not signed = false */
+	private boolean _isInTownInvasion = false;
+	
 	/** The Fame of this L2PcInstance */
 	private int _fame;
 	private ScheduledFuture<?> _fameTask;
@@ -5916,6 +5921,11 @@
 		{
 			reviveRequest(this, null, false);
 		}
+		// Prevents player from reviving to town if in town invasion event.
+		if (isInTownInvasion())
+		{
+			reviveToLocation(TownInvasionEvent.getInstance().getCurrentTownLocation());
+		}
 		return true;
 	}
 	
@@ -12564,6 +12574,9 @@
 			_log.log(Level.SEVERE, "deleteMe()", e);
 		}
 		
+		// XXX: Freedy - check if player is in TI event then remove him
+		TownInvasionEvent.getInstance().removePlayerFromEvent(this);
+		
 		// Update database with items in its inventory and remove them from the world
 		try
 		{
@@ -16427,4 +16440,40 @@
 	{
 		return _eventListeners;
 	}
+	
+	// XXX: Freedy
+	/**
+	 * @return True - if player is signed into town invasion
+	 */
+	public boolean isInTownInvasion()
+	{
+		return _isInTownInvasion;
+	}
+	
+	public void setIsInTownInvasion(boolean bool)
+	{
+		_isInTownInvasion = bool;
+		setCanRevive(!bool);
+	}
+	
+	/**
+	 * Revives L2PcInstance into King of town location, during current town invasion event<br>
+	 * Using random offset so players are not all at one place.
+	 * @param currentTownLocation - Location of King of town
+	 */
+	private void reviveToLocation(final Location currentTownLocation)
+	{
+		ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()
+		{
+			@Override
+			public void run()
+			{
+				if (!TownInvasionEvent.getInstance().isInActive())
+				{
+					teleToLocation(currentTownLocation, true);
+				}
+				doRevive();
+			}
+		}, Config.TI_RESPAWN * 1000);
+	}
 }
\ No newline at end of file
Index: java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionWave.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionWave.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionWave.java	(working copy)
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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.
+ * 
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity.TownInvasion;
+
+/**
+ * A class holding data for one town invasion wave.
+ * @author Freedy
+ */
+public class TownInvasionWave
+{
+	private final int _waveNumber;
+	private final int _monsterId;
+	private final boolean _isBoss;
+	
+	public TownInvasionWave(int waveNumber, int monster, boolean boss)
+	{
+		_waveNumber = waveNumber;
+		_monsterId = monster;
+		_isBoss = boss;
+	}
+	
+	/**
+	 * @return the _monster
+	 */
+	public int get_monster()
+	{
+		return _monsterId;
+	}
+	
+	/**
+	 * @return the _waveNumber
+	 */
+	public int get_waveNumber()
+	{
+		return _waveNumber;
+	}
+	
+	/**
+	 * @return the _waveNumber
+	 */
+	public boolean get_isBoss()
+	{
+		return _isBoss;
+	}
+	
+}
Index: java/com/l2jserver/gameserver/model/actor/instance/L2RaidBossInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2RaidBossInstance.java	(revision 6478)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2RaidBossInstance.java	(working copy)
@@ -124,7 +124,7 @@
 	 * Spawn all minions at a regular interval Also if boss is too far from home location at the time of this check, teleport it home.
 	 */
 	@Override
-	protected void startMaintenanceTask()
+	protected void startMaintanceTask()
 	{
 		if (getTemplate().getMinionData() != null)
 		{
Index: java/com/l2jserver/gameserver/model/actor/instance/L2TIRegNpcInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2TIRegNpcInstance.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2TIRegNpcInstance.java	(working copy)
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.instance;
+
+import com.l2jserver.gameserver.cache.HtmCache;
+import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
+import com.l2jserver.gameserver.model.entity.TownInvasion.TownInvasionEvent;
+import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
+import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
+
+public class L2TIRegNpcInstance extends L2Npc
+{
+	private final String htmlPath = "data/html/mods/TownInvasion/";
+	
+	public L2TIRegNpcInstance(int objectId, L2NpcTemplate template)
+	{
+		super(objectId, template);
+		setInstanceType(InstanceType.L2TIRegNpcInstance);
+	}
+	
+	@Override
+	public void onBypassFeedback(L2PcInstance playerInstance, String command)
+	{
+		TownInvasionEvent.getInstance().eventBypass(playerInstance, command);
+	}
+	
+	@Override
+	public void showChatWindow(L2PcInstance player, int val)
+	{
+		if (player == null)
+		{
+			return;
+		}
+		if (TownInvasionEvent.getInstance().isWaving() || TownInvasionEvent.getInstance().isLast())
+		{
+			final String htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), htmlPath + "started.htm");
+			
+			if (htmContent != null)
+			{
+				NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(getObjectId());
+				
+				npcHtmlMessage.setHtml(htmContent);
+				npcHtmlMessage.replace("%wave%", String.valueOf(TownInvasionEvent.getInstance().get_currentWaveNumber()) + ". wave");
+				npcHtmlMessage.replace("%town%", TownInvasionEvent.getInstance().getCurrentTownName());
+				player.sendPacket(npcHtmlMessage);
+			}
+		}
+		
+		else if (TownInvasionEvent.getInstance().isRegistration())
+		{
+			final String htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), htmlPath + "sign.htm");
+			
+			if (htmContent != null)
+			{
+				NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(getObjectId());
+				
+				npcHtmlMessage.setHtml(htmContent);
+				npcHtmlMessage.replace("%reg%", player.isInTownInvasion() ? "Remove registration" : "Register for event");
+				npcHtmlMessage.replace("%objectId%", String.valueOf(getObjectId()));
+				
+				player.sendPacket(npcHtmlMessage);
+			}
+		}
+		else
+		{
+			player.sendPacket(ActionFailed.STATIC_PACKET);
+		}
+	}
+}
Index: java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionManager.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionManager.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionManager.java	(working copy)
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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.
+ * 
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity.TownInvasion;
+
+import java.util.Calendar;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.ThreadPoolManager;
+
+/**
+ * A manager class working with TownInvasionEvent. It's used to schedule another event and to keep Task timing.
+ * @author Freedy
+ */
+public class TownInvasionManager
+{
+	protected static final Logger _log = Logger.getLogger(TownInvasionManager.class.getName());
+	private static TownInvasionTask _task;
+	
+	// constructor is not public, it's called with SingletonHolder
+	protected TownInvasionManager()
+	{
+		if (Config.TI_ENABLED)
+		{
+			TownInvasionEvent.getInstance();
+			if (TownInvasionEvent.getInstance().isLoaded())
+			{
+				scheduleTownInvasion();
+				_log.info("Town Invasion event engine started.");
+			}
+			else
+			{
+				_log.info("Town Invasion event engine is disabled. Town Invasion event isn't loaded.");
+			}
+		}
+		else
+		{
+			_log.info("Town Invasion event engine is disabled.");
+		}
+	}
+	
+	/**
+	 * Starts and schedules TownInvationTask.
+	 */
+	public void scheduleTownInvasion()
+	{
+		try
+		{
+			Calendar currentTime = Calendar.getInstance();
+			Calendar nextStartTime = null;
+			Calendar testStartTime = null;
+			for (String timeOfDay : Config.TI_SCHEDULE)
+			{
+				// Creating a Calendar object from the specified interval value
+				testStartTime = Calendar.getInstance();
+				testStartTime.setLenient(true);
+				String[] splitTimeOfDay = timeOfDay.split(":");
+				testStartTime.set(Calendar.HOUR_OF_DAY, Integer.parseInt(splitTimeOfDay[0]));
+				testStartTime.set(Calendar.MINUTE, Integer.parseInt(splitTimeOfDay[1]));
+				// If the date is in the past, make it the next day (Example: Checking for "1:00", when the time is 23:57.)
+				if (testStartTime.getTimeInMillis() < currentTime.getTimeInMillis())
+				{
+					testStartTime.add(Calendar.DAY_OF_MONTH, 1);
+				}
+				// Check for the test date to be the minimum (smallest in the specified list)
+				if ((nextStartTime == null) || (testStartTime.getTimeInMillis() < nextStartTime.getTimeInMillis()))
+				{
+					nextStartTime = testStartTime;
+				}
+			}
+			if (nextStartTime != null)
+			{
+				_log.log(Level.INFO, ("Next Town Invasion event will start in " + ((nextStartTime.getTimeInMillis() - currentTime.getTimeInMillis()) / 60000)) + " minutes.");
+				_task = new TownInvasionTask(nextStartTime.getTimeInMillis());
+				ThreadPoolManager.getInstance().scheduleGeneral(_task, (nextStartTime.getTimeInMillis() - currentTime.getTimeInMillis()));
+			}
+		}
+		catch (Exception e)
+		{
+			_log.warning(TownInvasionManager.class.getName() + " Error figuring out a start time. Check Town Invasion ScheduleTime in config file.");
+		}
+	}
+	
+	private static class SingletonHolder
+	{
+		protected static final TownInvasionManager _instance = new TownInvasionManager();
+	}
+	
+	/**
+	 * @return one and only instance of TownInvasionManager
+	 */
+	public static TownInvasionManager getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	/**
+	 * @param _task the _task to set
+	 */
+	public void set_task(TownInvasionTask _task)
+	{
+		TownInvasionManager._task = _task;
+	}
+	
+	/**
+	 * @param time - Time at which should be next start of event (milliseconds).
+	 */
+	public void setStartTime(long time)
+	{
+		get_task().setStartTime(time);
+	}
+	
+	/**
+	 * @return the _task from TownInvasionManager
+	 */
+	public TownInvasionTask get_task()
+	{
+		return _task;
+	}
+	
+}
Index: java/com/l2jserver/Config.java
===================================================================
--- java/com/l2jserver/Config.java	(revision 6478)
+++ java/com/l2jserver/Config.java	(working copy)
@@ -103,6 +103,8 @@
 	public static final String SECURITY_CONFIG_FILE = "./config/Security.properties";
 	public static final String EMAIL_CONFIG_FILE = "./config/Email.properties";
 	public static final String CH_SIEGE_FILE = "./config/ConquerableHallSiege.properties";
+	// Freedy
+	public static final String TOWN_INVASION_FILE = "./config/TownInvasion.properties";
 	// --------------------------------------------------
 	// L2J Variable Definitions
 	// --------------------------------------------------
@@ -833,6 +835,21 @@
 	public static List<Integer> NON_TALKING_NPCS;
 	
 	// --------------------------------------------------
+	// Town Invasion Settings XXX: Freedy
+	// --------------------------------------------------
+	public static boolean TI_ENABLED;
+	public static int TI_IMPACT_NPC_ID;
+	public static int TI_LIVES;
+	public static String[] TI_MOB_KILL_REWARD;
+	public static int TI_MOB_PER_PLAYER;
+	public static int TI_REG_NPC;
+	public static int TI_REG_TIME_MIN;
+	public static int TI_RESPAWN;
+	public static String[] TI_REWARDS;
+	public static String[] TI_SCHEDULE;
+	public static int TI_WAVE_TIME;
+	
+	// --------------------------------------------------
 	// PvP Settings
 	// --------------------------------------------------
 	public static int KARMA_MIN_KARMA;
@@ -2774,6 +2791,30 @@
 			}
 			L2JMOD_ALLOW_CHANGE_PASSWORD = Boolean.parseBoolean(L2JModSettings.getProperty("AllowChangePassword", "False"));
 			
+			// Load Town Invasion Properties file (if exists) XXX: Freedy
+			L2Properties townInvasionSettings = new L2Properties();
+			final File townInvasion = new File(TOWN_INVASION_FILE);
+			try (InputStream is = new FileInputStream(townInvasion))
+			{
+				townInvasionSettings.load(is);
+			}
+			catch (Exception e)
+			{
+				_log.log(Level.SEVERE, "Error while loading Town Invasion settings!", e);
+			}
+			
+			TI_ENABLED = Boolean.parseBoolean(townInvasionSettings.getProperty("EnableTownInvasion", "True"));
+			TI_LIVES = Integer.parseInt(townInvasionSettings.getProperty("TownInvasionLives", "20"));
+			TI_IMPACT_NPC_ID = Integer.parseInt(townInvasionSettings.getProperty("ImpactNpc", "33001"));
+			TI_MOB_KILL_REWARD = townInvasionSettings.getProperty("TIKillReward", "57,1000;57,2000").split(";");
+			TI_MOB_PER_PLAYER = Integer.parseInt(townInvasionSettings.getProperty("TIMobPerPlayer", "5"));
+			TI_REG_NPC = Integer.parseInt(townInvasionSettings.getProperty("TIRegistrationNpc", "33000"));
+			TI_REG_TIME_MIN = Integer.parseInt(townInvasionSettings.getProperty("TIRegistrationTime", "1"));
+			TI_RESPAWN = Integer.parseInt(townInvasionSettings.getProperty("TIDeadPlayerRespawn", "15"));
+			TI_REWARDS = townInvasionSettings.getProperty("TIRewards", "57,10000;57,20000").split(";");
+			TI_SCHEDULE = townInvasionSettings.getProperty("TIScheduleTimes", "12:00;14:00;16:00;18:00;20:00;22:00").split(";");
+			TI_WAVE_TIME = Integer.parseInt(townInvasionSettings.getProperty("TIWaveTime", "1"));
+			
 			// Load PvP L2Properties file (if exists)
 			final File pvp = new File(PVP_CONFIG_FILE);
 			L2Properties PVPSettings = new L2Properties();
Index: dist/game/config/TownInvasion.properties
===================================================================
--- dist/game/config/TownInvasion.properties	(revision 0)
+++ dist/game/config/TownInvasion.properties	(working copy)
@@ -0,0 +1,57 @@
+# ---------------------------------------------------------------------------
+# Town Invasion Event Settings
+# ---------------------------------------------------------------------------
+#
+# Enable Town Invasion event engine to run
+# Default: True
+EnableTownInvasion = True
+
+# Town Invasion lives for players
+# Example: If you set 50 then if players dont kill 50 mobs and they reach into impact point location they will loose and event will end.
+# Default: 20
+TownInvasionLives = 20
+
+# Town Invasion impact Npc
+# ID of NPC that represents final point of mobs coming to town
+# Default: 33001
+ImpactNpc = 33001
+
+# Kill reward
+# Reward for player when he kills one mob from wave
+# Default: 57,1000;
+TIKillReward = 57,1000;
+
+# Mobs per player
+# Decides how many mobs will spawn per wave on every signed player
+# Default: 5
+TIMobPerPlayer = 5
+
+# Registration Npc
+# Sets Npc id where players will sign
+# Default: 33000
+TIRegistrationNpc = 33000
+
+# Registration time
+# Defines for how long can players sign into event, then waves will start spawning. Time is in munites.
+# Default: 5
+TIRegistrationTime = 1
+
+# Player respawn delay
+# When player dies he will spawn back to impact point and go back into fight. Time is in seconds.
+TIDeadPlayerRespawn = 15
+
+# Finish rewards
+# When players succesfully kill all waves they get rewards.
+# Default: 57,10000;
+TIRewards = 57,10000;
+
+# Town invasion schedule
+# Set hour of day when TI should take place
+# Default: 12:00;14:00;16:00;18:00;20:00;22:00
+TIScheduleTimes = 12:00;14:00;16:00;18:00;20:00;22:00
+
+# Wave time
+# Delay between current wave spawns in minutes
+# Default: 5
+TIWaveTime = 1
+
Index: java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionState.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionState.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionState.java	(working copy)
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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.
+ * 
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity.TownInvasion;
+
+/**
+ * @author Freedy
+ */
+public enum TownInvasionState
+{
+	REGISTRATION,
+	INACTIVE,
+	WAVES,
+	LASTWAVE,
+	NOTLOADED,
+	END
+}
Index: java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionEvent.java
===================================================================
--- java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionEvent.java	(revision 0)
+++ java/com/l2jserver/gameserver/model/entity/TownInvasion/TownInvasionEvent.java	(working copy)
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2004-2014 L2J DataPack
+ * 
+ * This file is part of L2J DataPack.
+ * 
+ * L2J DataPack 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.
+ * 
+ * L2J DataPack is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.entity.TownInvasion;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.List;
+import java.util.logging.Level;
+
+import javolution.util.FastList;
+import javolution.util.FastMap;
+
+import com.l2jserver.Config;
+import com.l2jserver.L2DatabaseFactory;
+import com.l2jserver.gameserver.Announcements;
+import com.l2jserver.gameserver.ThreadPoolManager;
+import com.l2jserver.gameserver.model.Location;
+import com.l2jserver.gameserver.model.actor.L2Npc;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.quest.Event;
+import com.l2jserver.util.Rnd;
+
+/**
+ * Class extending Event. Contains methods to run town invasion event on server and to prepare it.
+ * @author Freedy
+ */
+public class TownInvasionEvent extends Event
+{
+	// SQL query strings
+	private final String _sqlLoadTown = "select * from town_invasion_towns";
+	private final String _sqlLoadWaves = "select * from town_invasion_waves order by wave_number asc";
+	
+	// parameters
+	private final List<L2PcInstance> _signedplayers;
+	private static TownInvasionState _state;
+	private final FastMap<Integer, TownInvasionWave> _waves = new FastMap<>();
+	private final FastMap<Integer, TownInvasionTown> _towns = new FastMap<>();
+	private static TownInvasionTown _currentTown;
+	private static TownInvasionWave _currentWave;
+	private static TownInvasionWave _nextWave;
+	private static int _playerLives;
+	private final List<L2Npc> _npclist;
+	
+	// currently spawned King of Town, who should be protected by players
+	private L2Npc _protecting = null;
+	
+	// constructor
+	public TownInvasionEvent(int questId, String name, String descr)
+	{
+		super(questId, name, descr);
+		_signedplayers = new FastList<>();
+		_npclist = new FastList<>();
+		_state = TownInvasionState.INACTIVE;
+		set_playerLives(Config.TI_LIVES);
+		if (!loadEvent())
+		{
+			set_state(TownInvasionState.NOTLOADED);
+		}
+	}
+	
+	@Override
+	public boolean eventStart()
+	{
+		// check if event is loaded, if not stop it permanently without next schedule
+		if (!isLoaded())
+		{
+			return false;
+		}
+		set_currentTown(_towns.get(Rnd.get(_towns.size())));
+		if (_currentTown == null)
+		{
+			eventStop();
+			return false;
+		}
+		
+		set_currentWave(_waves.get(1));
+		set_nextWave(_waves.get(2));
+		
+		_log.info("Choosen " + getCurrentTownName());
+		set_playerLives(Config.TI_LIVES);
+		
+		if (teleportPlayers())
+		{
+			_protecting = recordSpawn(Config.TI_IMPACT_NPC_ID, _currentTown.get_loc().getX(), _currentTown.get_loc().getY(), _currentTown.get_loc().getZ(), 0, false, 0);
+			Announcements.getInstance().announceToAll("Town Invasion: Teleporting signed players to " + getCurrentTownName() + ".", true);
+			Announcements.getInstance().announceToAll("Town Invasion: 1st wave will come in 1 minute. Prepare yourself!", true);
+			// check if there is only one wave in event and set it as last wave
+			if (_nextWave == null)
+			{
+				set_state(TownInvasionState.LASTWAVE);
+			}
+			else
+			{
+				set_state(TownInvasionState.WAVES);
+			}
+		}
+		else
+		{
+			// if no players signed, end event and schedule next start
+			Announcements.getInstance().announceToAll("Town Invasion: Nobody signed, event has been canceled.", true);
+			eventStop();
+			return false;
+		}
+		_log.log(Level.INFO, "Town Invasion: Event started.");
+		TownInvasionManager.getInstance().setStartTime(System.currentTimeMillis() + 60000);
+		ThreadPoolManager.getInstance().executeTask(TownInvasionManager.getInstance().get_task());
+		return true;
+	}
+	
+	@Override
+	public boolean eventStop()
+	{
+		if (!isLoaded())
+		{
+			_log.log(Level.WARNING, "Town invasion event is not loaded. Tasks will be stoped now.");
+			return false;
+		}
+		// reward players
+		if (checkLives())
+		{
+			for (L2PcInstance p : _signedplayers)
+			{
+				for (String s : Config.TI_REWARDS)
+				{
+					String item[] = s.split(",");
+					rewardItems(p, Integer.valueOf(item[0]), Integer.valueOf(item[1]));
+				}
+			}
+		}
+		else
+		{
+			Announcements.getInstance().announceToAll("Town Invasion: Players failed to protect " + getCurrentTownName() + ".");
+		}
+		// unspawn all npcs spawned by event
+		if (!_npclist.isEmpty())
+		{
+			for (L2Npc npc : _npclist)
+			{
+				npc.deleteMe();
+			}
+			_npclist.clear();
+			_protecting = null;
+			_log.info("Town Invasion: Cleaned Npcs.");
+		}
+		
+		// players cleanup after teleport back to sign NPC location
+		if (!_signedplayers.isEmpty())
+		{
+			for (L2PcInstance p : _signedplayers)
+			{
+				removePlayerFromEvent(p);
+				p.teleToLocation(82698, 148638, -3473, true);
+			}
+			_signedplayers.clear();
+		}
+		
+		_log.info("Town Invasion: Event ended, new schedule will be set now.");
+		set_state(TownInvasionState.INACTIVE);
+		
+		// remove current task from ThreadPoolManager and schedule next event start
+		TownInvasionManager.getInstance().get_task().cancel();
+		TownInvasionManager.getInstance().scheduleTownInvasion();
+		return false;
+	}
+	
+	@Override
+	public boolean eventBypass(L2PcInstance player, String bypass)
+	{
+		if (player == null)
+		{
+			return false;
+		}
+		
+		if (bypass.startsWith("ti_reg"))
+		{
+			if (player.isInTownInvasion())
+			{
+				removePlayerFromEvent(player);
+				player.sendMessage("You are now unregistred from Town invasion.");
+				return true;
+			}
+			_signedplayers.add(player);
+			player.setIsInTownInvasion(true);
+			player.sendMessage("You are now registred for Town invasion.");
+			return true;
+		}
+		return false;
+	}
+	
+	/**
+	 * @param state - the _state to set
+	 */
+	private void set_state(TownInvasionState state)
+	{
+		TownInvasionEvent._state = state;
+	}
+	
+	private static class SingletonHolder
+	{
+		protected static final TownInvasionEvent _instance = new TownInvasionEvent(-1, "TIEvent", "event");
+	}
+	
+	/**
+	 * @return One and only instance of TownInvasionEvent
+	 */
+	public static TownInvasionEvent getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	/**
+	 * Method loading waves and towns from database.
+	 * @return true - data were loaded successfully
+	 */
+	private boolean loadEvent()
+	{
+		try
+		{
+			// get connection to database and leave it open until load is finished
+			Connection con = L2DatabaseFactory.getInstance().getConnection();
+			PreparedStatement st_towns = con.prepareStatement(_sqlLoadTown);
+			ResultSet rs_towns = st_towns.executeQuery();
+			int towns = 0;
+			int spawn[] = new int[3];
+			String town_name;
+			_towns.clear();
+			// parse every row returned from database and save it into Map of towns
+			while (rs_towns.next())
+			{
+				spawn[0] = rs_towns.getInt("spawn_x");
+				spawn[1] = rs_towns.getInt("spawn_y");
+				spawn[2] = rs_towns.getInt("spawn_z");
+				Location impact = new Location(rs_towns.getInt("impact_x"), rs_towns.getInt("impact_y"), rs_towns.getInt("impact_z"));
+				town_name = rs_towns.getString("town_name");
+				
+				_towns.put(towns, new TownInvasionTown(town_name, spawn, impact));
+				towns++;
+			}
+			// add information about loading towns into log file
+			_log.log(Level.INFO, "Town invasion event: Loaded succesfully " + towns + " towns from database.");
+			
+			// prepare statement and get result of waves from database
+			PreparedStatement st_waves = con.prepareStatement(_sqlLoadWaves);
+			ResultSet rs_waves = st_waves.executeQuery();
+			
+			int wave_number = 0;
+			int monster_id = 0;
+			boolean isBoss = false;
+			_waves.clear();
+			// parse waves from database result and save them into Map of waves
+			while (rs_waves.next())
+			{
+				monster_id = rs_waves.getInt("monsterId");
+				wave_number = rs_waves.getInt("wave_number");
+				isBoss = (rs_waves.getInt("is_boss") == 0 ? false : true);
+				TownInvasionWave ti_wave = new TownInvasionWave(wave_number, monster_id, isBoss);
+				addWave(ti_wave);
+			}
+			// add information about loading waves into log file
+			_log.log(Level.INFO, "Town invasion event: Loaded succesfully " + _waves.size() + " waves from database.");
+			
+			// close database connection
+			con.close();
+			
+			// if the database load is successful, but there are no towns or waves loaded the state of event will remain NOTLOADED
+			if (_waves.isEmpty() || _towns.isEmpty())
+			{
+				return false;
+			}
+			return true;
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Error loading town invasion data.", e);
+			return false;
+		}
+	}
+	
+	/**
+	 * @param wave - TownInvasionWave to be added into list of waves
+	 */
+	private void addWave(TownInvasionWave wave)
+	{
+		_waves.put(wave.get_waveNumber(), wave);
+	}
+	
+	public boolean isRegistration()
+	{
+		return _state == TownInvasionState.REGISTRATION;
+	}
+	
+	public boolean isLoaded()
+	{
+		return _state != TownInvasionState.NOTLOADED;
+	}
+	
+	public boolean isInActive()
+	{
+		return _state == TownInvasionState.INACTIVE;
+	}
+	
+	public boolean isWaving()
+	{
+		return _state == TownInvasionState.WAVES;
+	}
+	
+	public boolean isLast()
+	{
+		return _state == TownInvasionState.LASTWAVE;
+	}
+	
+	/**
+	 * Start registration and schedule another task delay.
+	 */
+	public void startReg()
+	{
+		// save to log information about start of event
+		_log.log(Level.INFO, "Town Invasion registration started.");
+		set_state(TownInvasionState.REGISTRATION);
+		
+		// spawn registration NPC in Giran town
+		recordSpawn(Config.TI_REG_NPC, 82698, 148638, -3473, 0, false, 0);
+		// announce to players start of event
+		Announcements.getInstance().announceToAll("Town Invasion registration started. You have " + Config.TI_REG_TIME_MIN + " minute(s) to sign.", true);
+		TownInvasionManager.getInstance().setStartTime(System.currentTimeMillis() + (Config.TI_REG_TIME_MIN * 60000));
+		ThreadPoolManager.getInstance().executeTask(TownInvasionManager.getInstance().get_task());
+	}
+	
+	/**
+	 * @return wave_number of current TownInvasionWave
+	 */
+	public int get_currentWaveNumber()
+	{
+		return _currentWave.get_waveNumber();
+	}
+	
+	/**
+	 * Sets _currentWave to another instance of TownInvasionWave
+	 * @param wave - new _currentWave
+	 */
+	private void set_currentWave(TownInvasionWave wave)
+	{
+		TownInvasionEvent._currentWave = wave;
+	}
+	
+	/**
+	 * Sets _currentTown into another instance of TownInvasionTown
+	 * @param town - the _currentTown to set
+	 */
+	private void set_currentTown(TownInvasionTown town)
+	{
+		TownInvasionEvent._currentTown = town;
+	}
+	
+	/**
+	 * Sets _nextWave to another instance of TownInvasionWave
+	 * @param wave - new _nextWave
+	 */
+	private void set_nextWave(TownInvasionWave wave)
+	{
+		TownInvasionEvent._nextWave = wave;
+	}
+	
+	/**
+	 * @param _playerLives the _playerLives to set
+	 */
+	private void set_playerLives(int _playerLives)
+	{
+		TownInvasionEvent._playerLives = _playerLives;
+	}
+	
+	private int getSignedPlayersCount()
+	{
+		return _signedplayers.size();
+	}
+	
+	private L2Npc recordSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffSet, long despawnDelay)
+	{
+		L2Npc tmp = addSpawn(npcId, x, y, z, heading, false, despawnDelay);
+		if (tmp != null)
+		{
+			_npclist.add(tmp);
+		}
+		else
+		{
+			_log.info("Current mob doesn't exist. ID: " + npcId);
+		}
+		return tmp;
+	}
+	
+	/**
+	 * @return true if there are players to be ported and place where to port them, false otherwise
+	 */
+	private boolean teleportPlayers()
+	{
+		if (_signedplayers.isEmpty())
+		{
+			return false;
+		}
+		Location loc = _currentTown.get_loc();
+		if (loc == null)
+		{
+			_log.log(Level.WARNING, "Town Invasion: Undefined location.");
+			return false;
+		}
+		for (L2PcInstance p : _signedplayers)
+		{
+			p.teleToLocation(loc, true);
+		}
+		return true;
+	}
+	
+	public boolean spawnWave(boolean isLast)
+	{
+		
+		if (_currentWave == null)
+		{
+			_log.info("No current wave loaded");
+			return false;
+		}
+		_log.log(Level.INFO, "Town Invasion: Spawning wave " + _currentWave.get_waveNumber() + " and " + (_currentWave.get_isBoss() ? "is boss" : "is normal wave"));
+		
+		int x = 0;
+		int y = 0;
+		int z = _currentTown.get_spawnCoordinates()[2];
+		
+		if (!_currentWave.get_isBoss())
+		{
+			for (int i = 0; i < (Config.TI_MOB_PER_PLAYER * getSignedPlayersCount()); i++)
+			{
+				x = _currentTown.get_spawnCoordinates()[0] + (Rnd.get(-500, 500));
+				y = _currentTown.get_spawnCoordinates()[1] + (Rnd.get(-500, 500));
+				recordSpawn(_currentWave.get_monster(), x, y, z, 0, true, 0);
+			}
+		}
+		else
+		{
+			int bosses = Math.max(getSignedPlayersCount() / 10, 1);
+			for (int i = 0; i < bosses; i++)
+			{
+				x = _currentTown.get_spawnCoordinates()[0] + (Rnd.get(-500, 500));
+				y = _currentTown.get_spawnCoordinates()[1] + (Rnd.get(-500, 500));
+				recordSpawn(_currentWave.get_monster(), x, y, z, 0, true, 0);
+			}
+		}
+		Announcements.getInstance().announceToAll("Town invasion: Spawning new wave, get ready!", true);
+		
+		if (_nextWave != null)
+		{
+			_currentWave = _nextWave;
+		}
+		
+		if ((_currentWave.get_waveNumber() < _waves.size()) && (_nextWave != null))
+		{
+			_nextWave = (_waves.get(_currentWave.get_waveNumber() + 1));
+		}
+		else
+		{
+			_nextWave = null;
+			set_state(TownInvasionState.LASTWAVE);
+		}
+		
+		if (isLast)
+		{
+			// announce to players this is last wave
+			set_state(TownInvasionState.END);
+		}
+		TownInvasionManager.getInstance().setStartTime(System.currentTimeMillis() + (60000 * Config.TI_WAVE_TIME));
+		ThreadPoolManager.getInstance().executeTask(TownInvasionManager.getInstance().get_task());
+		return true;
+	}
+	
+	/**
+	 * @return True if _playerLives are still higher than 0. False otherwise.
+	 */
+	public boolean checkLives()
+	{
+		return _playerLives > 0;
+	}
+	
+	/**
+	 * Removes one live from _playerLives
+	 */
+	public void takeLives()
+	{
+		_playerLives--;
+		Announcements.getInstance().announceToAll("Town Invasion: One life has been taken! You have " + _playerLives + " life(s) left.", true);
+		_log.info("Town Invasion: One life has been taken. Remain: " + _playerLives);
+		if (!checkLives())
+		{
+			eventStop();
+		}
+	}
+	
+	/**
+	 * @param player - player instance to be checked
+	 * @return True if player is already in _signedplayers list
+	 */
+	public boolean isPlayerSigned(L2PcInstance player)
+	{
+		return _signedplayers.contains(player);
+	}
+	
+	/**
+	 * @return Name of currently chosen town
+	 */
+	public String getCurrentTownName()
+	{
+		return _currentTown.get_townName();
+	}
+	
+	/**
+	 * @return Impact location of currently chosen town
+	 */
+	public Location getCurrentTownLocation()
+	{
+		return _currentTown.get_loc();
+	}
+	
+	/**
+	 * Used to remove player from list of signed players. Also used in L2PcInstance when player is logging out to remove him.
+	 * @param player - L2PcInstance to be removed
+	 */
+	public void removePlayerFromEvent(L2PcInstance player)
+	{
+		if (_signedplayers.contains(player))
+		{
+			_signedplayers.remove(player);
+			player.setIsInTownInvasion(false);
+		}
+	}
+	
+	public L2Npc getKing()
+	{
+		return _protecting;
+	}
+	
+}
