ソースを参照

Retail like Clan Airships, thanks lion for info.
Also normal airship route reworked to quest.
Do not forget to update DP!

_DS_ 15 年 前
コミット
f3e0c05f92
30 ファイル変更1507 行追加426 行削除
  1. 234 22
      L2_GameServer/java/com/l2jserver/gameserver/instancemanager/AirShipManager.java
  2. 3 3
      L2_GameServer/java/com/l2jserver/gameserver/instancemanager/BoatManager.java
  3. 5 4
      L2_GameServer/java/com/l2jserver/gameserver/model/L2Object.java
  4. 8 0
      L2_GameServer/java/com/l2jserver/gameserver/model/L2World.java
  5. 174 43
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/L2Vehicle.java
  6. 0 83
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2AirShipControllerInstance.java
  7. 69 5
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2AirShipInstance.java
  8. 259 0
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableAirShipInstance.java
  9. 8 5
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java
  10. 130 115
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/knownlist/PcKnownList.java
  11. 40 0
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/stat/ControllableAirShipStat.java
  12. 1 1
      L2_GameServer/java/com/l2jserver/gameserver/model/actor/stat/VehicleStat.java
  13. 3 3
      L2_GameServer/java/com/l2jserver/gameserver/network/L2GamePacketHandler.java
  14. 94 4
      L2_GameServer/java/com/l2jserver/gameserver/network/SystemMessageId.java
  15. 3 0
      L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/Action.java
  16. 16 3
      L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/FinishRotating.java
  17. 134 0
      L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/MoveToLocationAirShip.java
  18. 21 1
      L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/RequestActionUse.java
  19. 24 22
      L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/RequestTargetCanceld.java
  20. 15 4
      L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/StartRotating.java
  21. 43 9
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/CharInfo.java
  22. 6 2
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/DeleteObject.java
  23. 52 41
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExAirShipInfo.java
  24. 67 0
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExAirShipTeleportList.java
  25. 15 9
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExMoveToLocationAirShip.java
  26. 13 8
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExStopMoveAirShip.java
  27. 39 15
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/UserInfo.java
  28. 14 13
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/VehicleDeparture.java
  29. 13 10
      L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/VehicleInfo.java
  30. 4 1
      L2_GameServer/java/com/l2jserver/gameserver/templates/skills/L2SkillType.java

+ 234 - 22
L2_GameServer/java/com/l2jserver/gameserver/instancemanager/AirShipManager.java

@@ -14,23 +14,39 @@
  */
 package com.l2jserver.gameserver.instancemanager;
 
-import java.util.ArrayList;
+import gnu.trove.TIntObjectHashMap;
 
-import javolution.util.FastMap;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
+import com.l2jserver.L2DatabaseFactory;
 import com.l2jserver.gameserver.idfactory.IdFactory;
-import com.l2jserver.gameserver.model.actor.instance.L2AirShipControllerInstance;
+import com.l2jserver.gameserver.model.VehiclePathPoint;
 import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2ControllableAirShipInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.serverpackets.ExAirShipTeleportList;
 import com.l2jserver.gameserver.templates.StatsSet;
 import com.l2jserver.gameserver.templates.chars.L2CharTemplate;
 
 
 public class AirShipManager
 {
+	private static final Logger _log = Logger.getLogger(AirShipManager.class.getName());
+
+	private static final String LOAD_DB = "SELECT * FROM airships";
+	private static final String ADD_DB = "INSERT INTO airships (owner_id,fuel) VALUES (?,?)";
+	private static final String UPDATE_DB = "UPDATE airships SET fuel=? WHERE owner_id=?";
+
 	private L2CharTemplate _airShipTemplate = null;
-	private FastMap<Integer, L2AirShipInstance> _airShips = new FastMap<Integer, L2AirShipInstance>();
-	private ArrayList<L2AirShipControllerInstance> _atcs = new ArrayList<L2AirShipControllerInstance>(2);
-	
+	private TIntObjectHashMap<StatsSet> _airShipsInfo = new TIntObjectHashMap<StatsSet>();
+	private TIntObjectHashMap<L2AirShipInstance> _airShips = new TIntObjectHashMap<L2AirShipInstance>();
+	private TIntObjectHashMap<AirShipTeleportList> _teleports = new TIntObjectHashMap<AirShipTeleportList>();
+
 	public static final AirShipManager getInstance()
 	{
 		return SingletonHolder._instance;
@@ -82,45 +98,241 @@ public class AirShipManager
 		npcDat.set("basePDef", 100);
 		npcDat.set("baseMDef", 100);
 		_airShipTemplate = new L2CharTemplate(npcDat);
+
+		load();
 	}
-	
-	public L2AirShipInstance getAirShip(int objectId)
+
+	public L2AirShipInstance getNewAirShip(int x, int y, int z, int heading)
 	{
-		return _airShips.get(objectId);
+		final L2AirShipInstance	airShip = new L2AirShipInstance(IdFactory.getInstance().getNextId(), _airShipTemplate);
+
+		airShip.setHeading(heading);
+		airShip.setXYZInvisible(x, y, z);
+		airShip.spawnMe();
+		airShip.getStat().setMoveSpeed(280);
+		airShip.getStat().setRotationSpeed(2000);
+		return airShip;
 	}
 
-	public L2AirShipInstance getNewAirShip(int x, int y, int z, int heading)
+	public L2AirShipInstance getNewAirShip(int x, int y, int z, int heading, int ownerId)
 	{
-		L2AirShipInstance airShip = new L2AirShipInstance(IdFactory.getInstance().getNextId(), _airShipTemplate);
-		_airShips.put(airShip.getObjectId(), airShip);
+		final StatsSet info = _airShipsInfo.get(ownerId);
+		if (info == null)
+			return null;
+
+		final L2AirShipInstance airShip;
+		if (_airShips.containsKey(ownerId))
+		{
+			airShip = _airShips.get(ownerId);
+			airShip.refreshID();
+		}
+		else
+		{
+			airShip = new L2ControllableAirShipInstance(IdFactory.getInstance().getNextId(), _airShipTemplate, ownerId);
+			_airShips.put(ownerId, airShip);
+
+			airShip.setMaxFuel(600);
+			airShip.setFuel(info.getInteger("fuel"));
+			airShip.getStat().setMoveSpeed(280);
+			airShip.getStat().setRotationSpeed(2000);
+		}
+
 		airShip.setHeading(heading);
 		airShip.setXYZInvisible(x, y, z);
 		airShip.spawnMe();
 		return airShip;
 	}
 
-	public void registerATC(L2AirShipControllerInstance atc)
+	public void removeAirShip(L2AirShipInstance ship)
 	{
-		_atcs.add(atc);
+		if (ship.getOwnerId() != 0)
+		{
+			storeInDb(ship.getOwnerId());
+			final StatsSet info = _airShipsInfo.get(ship.getOwnerId());
+			if (info != null)
+				info.set("fuel", ship.getFuel());
+		}
 	}
 
-	public ArrayList<L2AirShipControllerInstance> getATCs()
+	public boolean hasAirShipLicense(int ownerId)
 	{
-		return _atcs;
+		return _airShipsInfo.contains(ownerId);
 	}
 
-	public L2AirShipControllerInstance getNearestATC(L2AirShipInstance ship, int radius)
+	public void registerLicense(int ownerId)
 	{
-		if (_atcs == null || _atcs.isEmpty())
+		if (!_airShipsInfo.contains(ownerId))
+		{
+			final StatsSet info = new StatsSet();
+			info.set("fuel", 600);
+
+			_airShipsInfo.put(ownerId, info);
+
+			Connection con = null;
+			try
+			{
+				con = L2DatabaseFactory.getInstance().getConnection();
+
+				PreparedStatement statement = con.prepareStatement(ADD_DB);
+				statement.setInt(1, ownerId);
+				statement.setInt(2, info.getInteger("fuel"));
+				statement.executeUpdate();			
+				statement.close();
+			}
+			catch (SQLException e)
+			{
+				_log.log(Level.WARNING, getClass().getSimpleName()+": Could not add new airship license: " + e.getMessage(), e);
+			}
+			catch (Exception e)
+			{
+				_log.log(Level.WARNING, getClass().getSimpleName()+": Error while initializing: " + e.getMessage(), e);
+			}
+			finally
+			{
+				L2DatabaseFactory.close(con);
+			}
+		}
+	}
+
+	public boolean hasAirShip(int ownerId)
+	{
+		final L2AirShipInstance ship = _airShips.get(ownerId);
+		if (ship == null || !(ship.isVisible() || ship.isTeleporting()))
+			return false;
+
+		return true;
+	}
+
+	public void registerAirShipTeleportList(int dockId, int locationId, VehiclePathPoint[][] tp, int[] fuelConsumption)
+	{
+		if (tp.length != fuelConsumption.length)
+			return;
+
+		_teleports.put(dockId, new AirShipTeleportList(locationId, fuelConsumption, tp));
+	}
+
+	public void sendAirShipTeleportList(L2PcInstance player)
+	{
+		if (player == null || !player.isInAirShip())
+			return;
+
+		final L2AirShipInstance ship = player.getAirShip();
+		if (!ship.isCaptain(player) || !ship.isInDock() || ship.isMoving())
+			return;
+
+		int dockId = ship.getDockId();
+		if (!_teleports.contains(dockId))
+			return;
+
+		final AirShipTeleportList all = _teleports.get(dockId);
+		player.sendPacket(new ExAirShipTeleportList(all.location, all.routes, all.fuel));
+	}
+
+	public VehiclePathPoint[] getTeleportDestination(int dockId, int index)
+	{
+		final AirShipTeleportList all = _teleports.get(dockId);
+		if (all == null)
 			return null;
 
-		for (L2AirShipControllerInstance atc : _atcs)
+		if (index < -1 || index >= all.routes.length)
+			return null;
+
+		return all.routes[index + 1];
+	}
+
+	public int getFuelConsumption(int dockId, int index)
+	{
+		final AirShipTeleportList all = _teleports.get(dockId);
+		if (all == null)
+			return 0;
+
+		if (index < -1 || index >= all.fuel.length)
+			return 0;
+
+		return all.fuel[index + 1];
+	}
+
+	private void load()
+	{
+		Connection con = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+
+			PreparedStatement statement = con.prepareStatement(LOAD_DB);
+			ResultSet rset = statement.executeQuery();
+
+			while (rset.next())
+			{
+				StatsSet info = new StatsSet();
+				info.set("fuel", rset.getInt("fuel"));
+
+				_airShipsInfo.put(rset.getInt("owner_id"), info);
+				info = null;
+			}
+
+			rset.close();
+			statement.close();
+		}
+		catch (SQLException e)
+		{
+			_log.log(Level.WARNING, getClass().getSimpleName()+": Could not load airships table: " + e.getMessage(), e);
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, getClass().getSimpleName()+": Error while initializing: " + e.getMessage(), e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+
+		_log.info(getClass().getSimpleName()+": Loaded " + _airShipsInfo.size() + " private airships");
+	}
+
+	private void storeInDb(int ownerId)
+	{
+		StatsSet info = _airShipsInfo.get(ownerId);
+		if (info == null)
+			return;
+
+		Connection con = null;
+		try
 		{
-			if (atc != null && atc.isInsideRadius(ship, radius, true, false))
-				return atc;
+			con = L2DatabaseFactory.getInstance().getConnection();
+
+			PreparedStatement statement = con.prepareStatement(UPDATE_DB);
+			statement.setInt(1, info.getInteger("fuel"));
+			statement.setInt(2, ownerId);
+			statement.executeUpdate();
+			statement.close();
 		}
+		catch (SQLException e)
+		{
+			_log.log(Level.WARNING, getClass().getSimpleName()+": Could not update airships table: " + e.getMessage(), e);
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, getClass().getSimpleName()+": Error while save: " + e.getMessage(), e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+	}
+
+	private class AirShipTeleportList
+	{
+		public int location;
+		public int[] fuel;
+		public VehiclePathPoint[][] routes;
 
-		return null;
+		public AirShipTeleportList(int loc, int[] f, VehiclePathPoint[][] r)
+		{
+			location = loc;
+			fuel = f;
+			routes = r;
+		}
 	}
 
 	@SuppressWarnings("synthetic-access")

+ 3 - 3
L2_GameServer/java/com/l2jserver/gameserver/instancemanager/BoatManager.java

@@ -34,9 +34,9 @@ public class BoatManager
 	private Map<Integer, L2BoatInstance> _boats = new FastMap<Integer, L2BoatInstance>();
 	private boolean[] _docksBusy = new boolean[3];
 
-	public static final int TALKING_ISLAND = 0;
-	public static final int GLUDIN_HARBOR = 1;
-	public static final int RUNE_HARBOR = 2;
+	public static final int TALKING_ISLAND = 1;
+	public static final int GLUDIN_HARBOR = 2;
+	public static final int RUNE_HARBOR = 3;
 
 	private static final double BROADCAST_DISTANCE = 400000000; // 20000
 

+ 5 - 4
L2_GameServer/java/com/l2jserver/gameserver/model/L2Object.java

@@ -115,10 +115,11 @@ public abstract class L2Object
 		// Festival
 		L2FestivalGiudeInstance(L2Npc),
 		L2FestivalMonsterInstance(L2MonsterInstance),
-		// Ships and controllers
-		L2BoatInstance(L2Character),
-		L2AirShipInstance(L2Character),
-		L2AirShipControllerInstance(L2NpcInstance),
+		// Vehicles
+		L2Vehicle(L2Character),
+		L2BoatInstance(L2Vehicle),
+		L2AirShipInstance(L2Vehicle),
+		L2ControllableAirShipInstance(L2AirShipInstance),
 		// Siege
 		L2DefenderInstance(L2Attackable),
 		L2ArtefactInstance(L2NpcInstance),

+ 8 - 0
L2_GameServer/java/com/l2jserver/gameserver/model/L2World.java

@@ -40,6 +40,14 @@ public final class L2World
 {
 	private static Logger _log = Logger.getLogger(L2World.class.getName());
 
+	/**
+	 * Gracia border
+	 * Flying objects not allowed to the east of it.
+	 */
+	public static final int GRACIA_MAX_X = -166168;
+	public static final int GRACIA_MAX_Z = 6105;
+	public static final int GRACIA_MIN_Z = -895;
+
 	/*
 	* biteshift, defines number of regions
 	* note, shifting by 15 will result in regions corresponding to map tiles

+ 174 - 43
L2_GameServer/java/com/l2jserver/gameserver/model/actor/L2Vehicle.java

@@ -15,6 +15,9 @@
 package com.l2jserver.gameserver.model.actor;
 
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
 
 import javolution.util.FastList;
 
@@ -26,14 +29,16 @@ import com.l2jserver.gameserver.ai.L2CharacterAI;
 import com.l2jserver.gameserver.datatables.MapRegionTable;
 import com.l2jserver.gameserver.model.L2CharPosition;
 import com.l2jserver.gameserver.model.L2ItemInstance;
+import com.l2jserver.gameserver.model.L2World;
+import com.l2jserver.gameserver.model.L2WorldRegion;
 import com.l2jserver.gameserver.model.Location;
 import com.l2jserver.gameserver.model.VehiclePathPoint;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.actor.knownlist.VehicleKnownList;
 import com.l2jserver.gameserver.model.actor.stat.VehicleStat;
 import com.l2jserver.gameserver.network.SystemMessageId;
-import com.l2jserver.gameserver.network.serverpackets.DeleteObject;
 import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
+import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.templates.chars.L2CharTemplate;
 import com.l2jserver.gameserver.templates.item.L2Weapon;
@@ -44,17 +49,20 @@ import com.l2jserver.gameserver.util.Util;
  * @author DS
  * 
  */
-public class L2Vehicle extends L2Character
+public abstract class L2Vehicle extends L2Character
 {
+	protected int _dockId = 0;
 	protected final FastList<L2PcInstance> _passengers = new FastList<L2PcInstance>();
 	protected Location _oustLoc = null;
 	private Runnable _engine = null;
+
 	protected VehiclePathPoint[] _currentPath = null;
 	protected int _runState = 0;
 
 	public L2Vehicle(int objectId, L2CharTemplate template)
 	{
 		super(objectId, template);
+		setInstanceType(InstanceType.L2Vehicle);
 		setIsFlying(true);
 	}
 
@@ -68,10 +76,14 @@ public class L2Vehicle extends L2Character
 		return false;
 	}
 
+	public boolean canBeControlled()
+	{
+		return _engine == null;
+	}
+
 	public void registerEngine(Runnable r)
 	{
-		if (_engine == null)
-			_engine = r;
+		_engine = r;
 	}
 
 	public void runEngine(int delay)
@@ -82,13 +94,17 @@ public class L2Vehicle extends L2Character
 
 	public void executePath(VehiclePathPoint[] path)
 	{
-		_currentPath = path;
 		_runState = 0;
-		if (path != null && path.length > 0)
+		_currentPath = path;
+
+		if (_currentPath != null && _currentPath.length > 0)
 		{
 			final VehiclePathPoint point = _currentPath[0];
-			getStat().setMoveSpeed(point.moveSpeed);
-			getStat().setRotationSpeed(point.rotationSpeed);
+			if (point.moveSpeed > 0)
+				getStat().setMoveSpeed(point.moveSpeed);
+			if (point.rotationSpeed > 0)
+				getStat().setRotationSpeed(point.rotationSpeed);
+
 			getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(point.x, point.y, point.z, 0));
 			return;
 		}
@@ -99,33 +115,51 @@ public class L2Vehicle extends L2Character
 	public boolean moveToNextRoutePoint()
 	{
 		_move = null;
-		if (_currentPath == null)
-			return false;
 
-		_runState++;
-		if (_runState < _currentPath.length)
+		if (_currentPath != null)
 		{
-			final VehiclePathPoint point = _currentPath[_runState];
-			if (!isMovementDisabled())
+			_runState++;
+			if (_runState < _currentPath.length)
 			{
-				getStat().setMoveSpeed(point.moveSpeed);
-				getStat().setRotationSpeed(point.rotationSpeed);
-
-				MoveData m = new MoveData();
-				m.disregardingGeodata = false;
-				m.onGeodataPathIndex = -1;
-				m._xDestination = point.x;
-				m._yDestination = point.y;
-				m._zDestination = point.z;
-				m._heading = 0;
-				setHeading(Util.calculateHeadingFrom(getX(), getY(), point.x, point.y));
-
-				m._moveStartTime = GameTimeController.getGameTicks();
-				_move = m;
-
-				GameTimeController.getInstance().registerMovingObject(this);
-				return true;
+				final VehiclePathPoint point = _currentPath[_runState];
+				if (!isMovementDisabled())
+				{
+					if (point.moveSpeed == 0)
+					{
+						teleToLocation(point.x, point.y, point.z, point.rotationSpeed, false);
+						_currentPath = null;
+					}
+					else
+					{
+						if (point.moveSpeed > 0)
+							getStat().setMoveSpeed(point.moveSpeed);
+						if (point.rotationSpeed > 0)
+							getStat().setRotationSpeed(point.rotationSpeed);
+
+						MoveData m = new MoveData();
+						m.disregardingGeodata = false;
+						m.onGeodataPathIndex = -1;
+						m._xDestination = point.x;
+						m._yDestination = point.y;
+						m._zDestination = point.z;
+						m._heading = 0;
+
+						final double dx = point.x - getX();
+						final double dy = point.y - getY();
+						final double distance = Math.sqrt(dx*dx + dy*dy);
+						if (distance > 1) // vertical movement heading check
+							setHeading(Util.calculateHeadingFrom(getX(), getY(), point.x, point.y));
+
+						m._moveStartTime = GameTimeController.getGameTicks();
+						_move = m;
+
+						GameTimeController.getInstance().registerMovingObject(this);
+						return true;
+					}
+				}
 			}
+			else
+				_currentPath = null;
 		}
 
 		runEngine(10);
@@ -150,6 +184,21 @@ public class L2Vehicle extends L2Character
 		setStat(new VehicleStat(this));
 	}
 
+	public boolean isInDock()
+	{
+		return _dockId > 0;
+	}
+
+	public int getDockId()
+	{
+		return _dockId;
+	}
+
+	public void setInDock(int d)
+	{
+		_dockId = d;
+	}
+
 	public void setOustLoc(Location loc)
 	{
 		_oustLoc = loc;
@@ -162,9 +211,15 @@ public class L2Vehicle extends L2Character
 
 	public void oustPlayers()
 	{
-		if (!_passengers.isEmpty())
+		L2PcInstance player;
+
+		// Use iterator because oustPlayer will try to remove player from _passengers 
+		final Iterator<L2PcInstance> iter = _passengers.iterator();
+		while (iter.hasNext())
 		{
-			for (L2PcInstance player : _passengers)
+			player = iter.next();
+			iter.remove();
+			if (player != null)
 				oustPlayer(player);
 		}
 	}
@@ -173,6 +228,7 @@ public class L2Vehicle extends L2Character
 	{
 		player.setVehicle(null);
 		player.setInVehiclePosition(null);
+		removePassenger(player);
 	}
 
 	public boolean addPassenger(L2PcInstance player)
@@ -180,6 +236,10 @@ public class L2Vehicle extends L2Character
 		if (player == null || _passengers.contains(player))
 			return false;
 
+		// already in other vehicle
+		if (player.getVehicle() != null && player.getVehicle() != this)
+			return false;
+
 		_passengers.add(player);
 		return true;
 	}
@@ -194,6 +254,25 @@ public class L2Vehicle extends L2Character
 		{}
 	}
 
+	public boolean isEmpty()
+	{
+		return _passengers.isEmpty();
+	}
+
+	public List<L2PcInstance> getPassengers()
+	{
+		return _passengers;
+	}
+
+	public void broadcastToPassengers(L2GameServerPacket sm)
+	{
+		for (L2PcInstance player : _passengers)
+		{
+			if (player != null)
+				player.sendPacket(sm);
+		}
+	}
+
 	/**
 	 * Consume ticket(s) and teleport player from boat if no correct ticket
 	 * @param itemId Ticket itemId
@@ -238,24 +317,25 @@ public class L2Vehicle extends L2Character
 	public boolean updatePosition(int gameTicks)
 	{
 		final boolean result = super.updatePosition(gameTicks);
-		if (!_passengers.isEmpty())
+
+		for (L2PcInstance player : _passengers)
 		{
-			for (L2PcInstance player : _passengers)
+			if (player != null && player.getVehicle() == this)
 			{
-				if (player != null && player.getVehicle() == this)
-				{
-					player.getPosition().setXYZ(getX(), getY(), getZ());
-					player.revalidateZone(false);
-				}
+				player.getPosition().setXYZ(getX(), getY(), getZ());
+				player.revalidateZone(false);
 			}
 		}
+
 		return result;
 	}
 
 	@Override
 	public void teleToLocation(int x, int y, int z, int heading, boolean allowRandomOffset)
 	{
-		stopMove(null, false);
+		if (isMoving())
+			stopMove(null, false);
+
 		setIsTeleporting(true);
 
 		getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
@@ -266,8 +346,6 @@ public class L2Vehicle extends L2Character
 				player.teleToLocation(x, y, z);
 		}
 
-		broadcastPacket(new DeleteObject(this));
-
 		decayMe();
 		setXYZ(x, y, z);
 
@@ -294,6 +372,59 @@ public class L2Vehicle extends L2Character
 			this.getKnownList().findObjects();
 	}
 
+	@Override
+	public void deleteMe()
+	{
+		_engine = null;
+
+		try
+		{
+			if (isMoving())
+				stopMove(null);
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Failed stopMove().", e);
+		}
+
+		try
+		{
+			oustPlayers();
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Failed oustPlayers().", e);
+		}
+
+		final L2WorldRegion oldRegion = getWorldRegion();
+
+		try
+		{
+			decayMe();
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Failed decayMe().", e);
+		}
+
+		if (oldRegion != null)
+			oldRegion.removeFromZones(this);
+
+		try
+		{
+			getKnownList().removeAllKnownObjects();
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Failed cleaning knownlist.", e);
+		}
+
+		// Remove L2Object object from _allObjects of L2World
+		L2World.getInstance().removeObject(this);
+
+		super.deleteMe();
+	}
+
 	@Override
 	public void updateAbnormalEffect()
 	{

+ 0 - 83
L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2AirShipControllerInstance.java

@@ -1,83 +0,0 @@
-/*
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- * 
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- * 
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model.actor.instance;
-
-import com.l2jserver.gameserver.instancemanager.AirShipManager;
-import com.l2jserver.gameserver.network.SystemMessageId;
-import com.l2jserver.gameserver.network.serverpackets.NpcSay;
-import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
-import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
-
-/**
- * NPC to control passengers stepping in/out of the airship
- *
- * @author  DrHouse
- */
-public class L2AirShipControllerInstance extends L2NpcInstance
-{
-	private L2AirShipInstance _ship = null;
-
-	/**
-	 * @param objectId
-	 * @param template
-	 */
-	public L2AirShipControllerInstance(int objectId, L2NpcTemplate template)
-	{
-		super(objectId, template);
-		setInstanceType(InstanceType.L2AirShipControllerInstance);
-		AirShipManager.getInstance().registerATC(this);
-	}
-
-	@Override
-	public void onBypassFeedback(L2PcInstance player, String command)
-	{
-		if (command.equalsIgnoreCase("board"))
-		{
-			if (player.getPet() != null)
-			{
-				player.sendPacket(new SystemMessage(SystemMessageId.RELEASE_PET_ON_BOAT));
-				return;
-			}
-			if (player.isTransformed())
-			{
-				player.sendPacket(new SystemMessage(SystemMessageId.CANT_POLYMORPH_ON_BOAT));
-				return;
-			}
-			if (player.isFlyingMounted())
-			{
-				player.sendPacket(new SystemMessage(SystemMessageId.YOU_CANNOT_MOUNT_NOT_MEET_REQUEIREMENTS));
-				return;
-			}
-
-			if (_ship != null)
-			{
-				_ship.addPassenger(player);
-				return;
-			}
-		}
-		else
-			super.onBypassFeedback(player, command);
-	}
-
-	public void broadcastMessage(String message)
-	{
-		broadcastPacket(new NpcSay(getObjectId(), 1, getNpcId(), message));
-	}
-
-	public void dockShip(L2AirShipInstance ship)
-	{
-		_ship = ship;
-	}
-}

+ 69 - 5
L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2AirShipInstance.java

@@ -14,9 +14,8 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
-import java.util.logging.Logger;
-
 import com.l2jserver.gameserver.ai.L2AirShipAI;
+import com.l2jserver.gameserver.instancemanager.AirShipManager;
 import com.l2jserver.gameserver.model.L2CharPosition;
 import com.l2jserver.gameserver.model.Location;
 import com.l2jserver.gameserver.model.actor.L2Vehicle;
@@ -35,8 +34,6 @@ import com.l2jserver.util.Point3D;
  */
 public class L2AirShipInstance extends L2Vehicle
 {
-	protected static final Logger _airShiplog = Logger.getLogger(L2AirShipInstance.class.getName());
-	
 	public L2AirShipInstance(int objectId, L2CharTemplate template)
 	{
 		super(objectId, template);
@@ -50,6 +47,61 @@ public class L2AirShipInstance extends L2Vehicle
 		return true;
 	}
 
+	public boolean isOwner(L2PcInstance player)
+	{
+		return false;
+	}
+
+	public int getOwnerId()
+	{
+		return 0;
+	}
+
+	public boolean isCaptain(L2PcInstance player)
+	{
+		return false;
+	}
+
+	public int getCaptainId()
+	{
+		return 0;
+	}
+
+	public int getHelmObjectId()
+	{
+		return 0;
+	}
+
+	public int getHelmItemId()
+	{
+		return 0;
+	}
+
+	public boolean setCaptain(L2PcInstance player)
+	{
+		return false;
+	}
+
+	public int getFuel()
+	{
+		return 0;
+	}
+
+	public void setFuel(int f)
+	{
+		
+	}
+
+	public int getMaxFuel()
+	{
+		return 0;
+	}
+
+	public void setMaxFuel(int mf)
+	{
+		
+	}
+
 	@Override
 	public boolean moveToNextRoutePoint()
 	{
@@ -79,7 +131,6 @@ public class L2AirShipInstance extends L2Vehicle
 	public void oustPlayer(L2PcInstance player)
 	{
 		super.oustPlayer(player);
-
 		final Location loc = getOustLoc();
 		if (player.isOnline() > 0)
 		{
@@ -92,6 +143,13 @@ public class L2AirShipInstance extends L2Vehicle
 			player.setXYZInvisible(loc.getX(), loc.getY(), loc.getZ());
 	}
 
+	@Override
+	public void deleteMe()
+	{
+		super.deleteMe();
+		AirShipManager.getInstance().removeAirShip(this);
+	}
+
 	@Override
 	public void stopMove(L2CharPosition pos, boolean updateKnownObjects)
 	{
@@ -100,6 +158,12 @@ public class L2AirShipInstance extends L2Vehicle
 		broadcastPacket(new ExStopMoveAirShip(this));
 	}
 
+	@Override
+	public void updateAbnormalEffect()
+	{
+		broadcastPacket(new ExAirShipInfo(this));
+	}
+
 	@Override
 	public void sendInfo(L2PcInstance activeChar)
 	{

+ 259 - 0
L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableAirShipInstance.java

@@ -0,0 +1,259 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.instance;
+
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+
+import com.l2jserver.gameserver.ThreadPoolManager;
+import com.l2jserver.gameserver.idfactory.IdFactory;
+import com.l2jserver.gameserver.model.actor.stat.ControllableAirShipStat;
+import com.l2jserver.gameserver.network.SystemMessageId;
+import com.l2jserver.gameserver.network.serverpackets.DeleteObject;
+import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected;
+import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
+import com.l2jserver.gameserver.templates.chars.L2CharTemplate;
+
+public class L2ControllableAirShipInstance extends L2AirShipInstance
+{
+	private static final int HELM = 13556;
+	private static final int LOW_FUEL = 40;
+
+	private int _fuel = 0;
+	private int _maxFuel = 0;
+
+	private int _ownerId;
+	private int _helmId;
+	private L2PcInstance _captain = null;
+
+	private Future<?> _consumeFuelTask;
+	private Future<?> _decayTask;
+
+	public L2ControllableAirShipInstance(int objectId, L2CharTemplate template, int ownerId)
+	{
+		super(objectId, template);
+		setInstanceType(InstanceType.L2ControllableAirShipInstance);
+		_ownerId = ownerId;
+		_helmId = IdFactory.getInstance().getNextId(); // not forget to release !
+		_decayTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new DecayTask(), 60000, 10000);
+		_consumeFuelTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new ConsumeFuelTask(), 60000, 60000);
+	}
+
+	@Override
+	public ControllableAirShipStat getStat()
+	{
+		return (ControllableAirShipStat)super.getStat();
+	}
+
+	@Override
+	public void initCharStat()
+	{
+		setStat(new ControllableAirShipStat(this));
+	}
+
+	@Override
+	public boolean canBeControlled()
+	{
+		return super.canBeControlled() && !isInDock();
+	}
+
+	@Override
+	public boolean isOwner(L2PcInstance player)
+	{
+		if (_ownerId == 0)
+			return false;
+
+		return player.getClanId() == _ownerId || player.getObjectId() == _ownerId;
+	}
+
+	@Override
+	public int getOwnerId()
+	{
+		return _ownerId;
+	}
+
+	@Override
+	public boolean isCaptain(L2PcInstance player)
+	{
+		return _captain != null && player == _captain;
+	}
+
+	@Override
+	public int getCaptainId()
+	{
+		return _captain != null ? _captain.getObjectId() : 0;
+	}
+
+	@Override
+	public int getHelmObjectId()
+	{
+		return _helmId;
+	}
+
+	@Override
+	public int getHelmItemId()
+	{
+		return HELM;
+	}
+
+	@Override
+	public boolean setCaptain(L2PcInstance player)
+	{
+		if (player == null)
+			_captain = null;
+		else
+		{
+			if (_captain == null && player.getAirShip() == this)
+			{
+				final int x = player.getInVehiclePosition().getX() - 0x16e;
+				final int y = player.getInVehiclePosition().getY();
+				final int z = player.getInVehiclePosition().getZ() - 0x6b;
+				if (x * x + y * y + z * z > 2500)
+				{
+					player.sendPacket(new SystemMessage(SystemMessageId.CANT_CONTROL_TOO_FAR));
+					return false;
+				}
+				_captain = player;
+				player.broadcastUserInfo();
+			}
+			else
+				return false;
+		}
+		updateAbnormalEffect();
+		return true;
+	}
+
+	@Override
+	public int getFuel()
+	{
+		return _fuel;
+	}
+
+	@Override
+	public void setFuel(int f)
+	{
+
+		final int old = _fuel;
+		if (f < 0)
+			_fuel = 0;
+		else if (f > _maxFuel)
+			_fuel = _maxFuel;
+		else
+			_fuel = f;
+
+		if (_fuel == 0 && old > 0)
+			broadcastToPassengers(new SystemMessage(SystemMessageId.THE_AIRSHIP_FUEL_RUN_OUT));
+		else if (_fuel < LOW_FUEL)
+			broadcastToPassengers(new SystemMessage(SystemMessageId.THE_AIRSHIP_FUEL_SOON_RUN_OUT));
+	}
+
+	@Override
+	public int getMaxFuel()
+	{
+		return _maxFuel;
+	}
+
+	@Override
+	public void setMaxFuel(int mf)
+	{
+		_maxFuel = mf;
+	}
+
+	@Override
+	public void oustPlayer(L2PcInstance player)
+	{
+		if (player == _captain)
+			setCaptain(null); // no need to broadcast userinfo here
+
+		super.oustPlayer(player);
+	}
+
+	@Override
+	public void onAction(L2PcInstance player, boolean interact)
+	{
+		player.sendPacket(new MyTargetSelected(_helmId, 0));
+		super.onAction(player, interact);
+	}
+
+	@Override
+	public void deleteMe()
+	{
+		if (_decayTask != null)
+		{
+			_decayTask.cancel(false);
+			_decayTask = null;
+		}
+		if (_consumeFuelTask != null)
+		{
+			_consumeFuelTask.cancel(false);
+			_consumeFuelTask = null;
+		}
+
+		try
+		{
+			broadcastPacket(new DeleteObject(_helmId));
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Failed decayMe():"+e.getMessage());
+		}
+
+		super.deleteMe();
+	}
+
+	@Override
+	public void refreshID()
+	{
+		super.refreshID();
+		IdFactory.getInstance().releaseId(_helmId);
+		_helmId = IdFactory.getInstance().getNextId();
+	}
+
+	@Override
+	public void sendInfo(L2PcInstance activeChar)
+	{
+		super.sendInfo(activeChar);
+		if (_captain != null)
+			_captain.sendInfo(activeChar);
+	}
+
+	private class ConsumeFuelTask implements Runnable
+	{
+		public void run()
+		{
+			int fuel = getFuel();
+			if (fuel > 0)
+			{
+				fuel -= 10;
+				if (fuel < 0)
+					fuel = 0;
+
+				setFuel(fuel);
+				updateAbnormalEffect();
+			}
+		}
+	}
+
+	private class DecayTask implements Runnable
+	{
+		public void run()
+		{
+			if (isVisible()
+					&& isEmpty()
+					&& !isInDock())
+				deleteMe();
+		}
+	}
+}

+ 8 - 5
L2_GameServer/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

@@ -4323,7 +4323,7 @@ public final class L2PcInstance extends L2Playable
 		final int charges = getCharges();
 		// Check if the spell using charges or not in AirShip
 		if ((skill.getMaxCharges() == 0 && charges < skill.getNumCharges())
-				|| isInAirShip())
+				|| (isInAirShip() && skill.getSkillType() != L2SkillType.REFUEL))
 		{
 			SystemMessage sm = new SystemMessage(SystemMessageId.S1_CANNOT_BE_USED);
 			sm.addSkillName(skill);
@@ -5143,6 +5143,10 @@ public final class L2PcInstance extends L2Playable
 			if((newTarget instanceof L2FestivalMonsterInstance) && !isFestivalParticipant())
 				newTarget = null;
 
+			// vehicles cant be targeted
+			else if (newTarget instanceof L2Vehicle)
+				newTarget = null;
+
 			// Can't target and attack rift invaders if not in the same room
 			else if(isInParty() && getParty().isInDimensionalRift())
 			{
@@ -11804,10 +11808,9 @@ public final class L2PcInstance extends L2Playable
 			// before entering in observer mode
 			if (inObserverMode())
 				setXYZInvisible(_obsX, _obsY, _obsZ);
-			else if (isInAirShip())
-				getAirShip().oustPlayer(this);
-			else if (isInBoat())
-				getBoat().oustPlayer(this);
+
+			if (getVehicle() != null)
+				getVehicle().oustPlayer(this);
 		}
 		catch (Exception e)
 		{

+ 130 - 115
L2_GameServer/java/com/l2jserver/gameserver/model/actor/knownlist/PcKnownList.java

@@ -20,129 +20,144 @@ import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.actor.L2Npc;
 import com.l2jserver.gameserver.model.actor.L2Vehicle;
+import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.network.serverpackets.DeleteObject;
 import com.l2jserver.gameserver.network.serverpackets.SpawnItem;
 
 public class PcKnownList extends PlayableKnownList
 {
-    // =========================================================
-    // Data Field
-
-    // =========================================================
-    // Constructor
-    public PcKnownList(L2PcInstance activeChar)
-    {
-        super(activeChar);
-    }
-
-    // =========================================================
-    // Method - Public
-    /**
-     * Add a visible L2Object to L2PcInstance _knownObjects and _knownPlayer (if necessary) and send Server-Client Packets needed to inform the L2PcInstance of its state and actions in progress.<BR><BR>
-     *
-     * <B><U> object is a L2ItemInstance </U> :</B><BR><BR>
-     * <li> Send Server-Client Packet DropItem/SpawnItem to the L2PcInstance </li><BR><BR>
-     *
-     * <B><U> object is a L2DoorInstance </U> :</B><BR><BR>
-     * <li> Send Server-Client Packets DoorInfo and DoorStatusUpdate to the L2PcInstance </li>
-     * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
-     *
-     * <B><U> object is a L2NpcInstance </U> :</B><BR><BR>
-     * <li> Send Server-Client Packet NpcInfo to the L2PcInstance </li>
-     * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
-     *
-     * <B><U> object is a L2Summon </U> :</B><BR><BR>
-     * <li> Send Server-Client Packet NpcInfo/PetItemList (if the L2PcInstance is the owner) to the L2PcInstance </li>
-     * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
-     *
-     * <B><U> object is a L2PcInstance </U> :</B><BR><BR>
-     * <li> Send Server-Client Packet CharInfo to the L2PcInstance </li>
-     * <li> If the object has a private store, Send Server-Client Packet PrivateStoreMsgSell to the L2PcInstance </li>
-     * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
-     *
-     * @param object The L2Object to add to _knownObjects and _knownPlayer
-     * @param dropper The L2Character who dropped the L2Object
-     */
-    @Override
+	public PcKnownList(L2PcInstance activeChar)
+	{
+		super(activeChar);
+	}
+
+	/**
+	 * Add a visible L2Object to L2PcInstance _knownObjects and _knownPlayer (if necessary) and send Server-Client Packets needed to inform the L2PcInstance of its state and actions in progress.<BR><BR>
+	 *
+	 * <B><U> object is a L2ItemInstance </U> :</B><BR><BR>
+	 * <li> Send Server-Client Packet DropItem/SpawnItem to the L2PcInstance </li><BR><BR>
+	 *
+	 * <B><U> object is a L2DoorInstance </U> :</B><BR><BR>
+	 * <li> Send Server-Client Packets DoorInfo and DoorStatusUpdate to the L2PcInstance </li>
+	 * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
+	 *
+	 * <B><U> object is a L2NpcInstance </U> :</B><BR><BR>
+	 * <li> Send Server-Client Packet NpcInfo to the L2PcInstance </li>
+	 * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
+	 *
+	 * <B><U> object is a L2Summon </U> :</B><BR><BR>
+	 * <li> Send Server-Client Packet NpcInfo/PetItemList (if the L2PcInstance is the owner) to the L2PcInstance </li>
+	 * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
+	 *
+	 * <B><U> object is a L2PcInstance </U> :</B><BR><BR>
+	 * <li> Send Server-Client Packet CharInfo to the L2PcInstance </li>
+	 * <li> If the object has a private store, Send Server-Client Packet PrivateStoreMsgSell to the L2PcInstance </li>
+	 * <li> Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance </li><BR><BR>
+	 *
+	 * @param object The L2Object to add to _knownObjects and _knownPlayer
+	 * @param dropper The L2Character who dropped the L2Object
+	 */
+	@Override
 	public boolean addKnownObject(L2Object object)
-    {
-        if (!super.addKnownObject(object)) return false;
-
-        if (object.getPoly().isMorphed() && object.getPoly().getPolyType().equals("item"))
-        {
-            //if (object.getPolytype().equals("item"))
-                getActiveChar().sendPacket(new SpawnItem(object));
-            //else if (object.getPolytype().equals("npc"))
-            //    sendPacket(new NpcInfoPoly(object, this));
-
-        }
-        else
-        {
-        	object.sendInfo(getActiveChar());
-
-            if (object instanceof L2Character)
-            {
-                // Update the state of the L2Character object client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance
-                L2Character obj = (L2Character) object;
-                if (obj.getAI() != null)
-                	obj.getAI().describeStateToPlayer(getActiveChar());
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Remove a L2Object from L2PcInstance _knownObjects and _knownPlayer (if necessary) and send Server-Client Packet DeleteObject to the L2PcInstance.<BR><BR>
-     *
-     * @param object The L2Object to remove from _knownObjects and _knownPlayer
-     *
-     */
-    @Override
+	{
+		if (!super.addKnownObject(object))
+			return false;
+
+		if (object.getPoly().isMorphed()
+				&& object.getPoly().getPolyType().equals("item"))
+		{
+			//if (object.getPolytype().equals("item"))
+			getActiveChar().sendPacket(new SpawnItem(object));
+			//else if (object.getPolytype().equals("npc"))
+			//    sendPacket(new NpcInfoPoly(object, this));
+		}
+		else
+		{
+			object.sendInfo(getActiveChar());
+
+			if (object instanceof L2Character)
+			{
+				// Update the state of the L2Character object client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance
+				L2Character obj = (L2Character) object;
+				if (obj.hasAI())
+					obj.getAI().describeStateToPlayer(getActiveChar());
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Remove a L2Object from L2PcInstance _knownObjects and _knownPlayer (if necessary) and send Server-Client Packet DeleteObject to the L2PcInstance.<BR><BR>
+	 *
+	 * @param object The L2Object to remove from _knownObjects and _knownPlayer
+	 *
+	 */
+	@Override
 	protected boolean removeKnownObject(L2Object object, boolean forget)
-    {
-            if (!super.removeKnownObject(object, forget)) return false;
-        // Send Server-Client Packet DeleteObject to the L2PcInstance
-        getActiveChar().sendPacket(new DeleteObject(object));
-       if (Config.CHECK_KNOWN && object instanceof L2Npc) getActiveChar().sendMessage("Removed NPC: "+((L2Npc)object).getName());
-        return true;
-    }
-
-    // =========================================================
-    // Method - Private
-
-    // =========================================================
-    // Property - Public
-    @Override
-	public final L2PcInstance getActiveChar() { return (L2PcInstance)super.getActiveChar(); }
-
-    @Override
+	{
+		if (!super.removeKnownObject(object, forget))
+			return false;
+
+		if (object instanceof L2AirShipInstance)
+		{
+			if (((L2AirShipInstance)object).getCaptainId() != 0
+					&& ((L2AirShipInstance)object).getCaptainId() != getActiveChar().getObjectId())
+				getActiveChar().sendPacket(new DeleteObject(((L2AirShipInstance)object).getCaptainId()));
+			if (((L2AirShipInstance)object).getHelmObjectId() != 0)
+				getActiveChar().sendPacket(new DeleteObject(((L2AirShipInstance)object).getHelmObjectId()));
+		}
+
+			// Send Server-Client Packet DeleteObject to the L2PcInstance
+			getActiveChar().sendPacket(new DeleteObject(object));
+
+		if (Config.CHECK_KNOWN && object instanceof L2Npc)
+			getActiveChar().sendMessage("Removed NPC: "+((L2Npc)object).getName());
+
+		return true;
+	}
+
+	@Override
+	public final L2PcInstance getActiveChar()
+	{
+		return (L2PcInstance)super.getActiveChar();
+	}
+
+	@Override
 	public int getDistanceToForgetObject(L2Object object)
-    {
-    	if (object instanceof L2Vehicle)
-    		return 10000;
-
-    	// when knownlist grows, the distance to forget should be at least
-    	// the same as the previous watch range, or it becomes possible that
-    	// extra charinfo packets are being sent (watch-forget-watch-forget)
-    	final int knownlistSize = getKnownObjects().size();
-        if (knownlistSize <= 25) return 4000;
-        if (knownlistSize <= 35) return 3500;
-        if (knownlistSize <= 70) return 2910;
-        else return 2310;
-    }
-
-    @Override
+	{
+		if (object instanceof L2Vehicle)
+			return 10000;
+
+		// when knownlist grows, the distance to forget should be at least
+		// the same as the previous watch range, or it becomes possible that
+		// extra charinfo packets are being sent (watch-forget-watch-forget)
+		final int knownlistSize = getKnownObjects().size();
+		if (knownlistSize <= 25)
+			return 4000;
+		if (knownlistSize <= 35)
+			return 3500;
+		if (knownlistSize <= 70)
+			return 2910;
+		else
+			return 2310;
+	}
+
+	@Override
 	public int getDistanceToWatchObject(L2Object object)
-    {
-    	if (object instanceof L2Vehicle)
-    		return 8000;
-
-    	final int knownlistSize = getKnownObjects().size();
-        if (knownlistSize <= 25) return 3400; // empty field
-        if (knownlistSize <= 35) return 2900;
-        if (knownlistSize <= 70) return 2300;
-        else return 1700; // Siege, TOI, city
-    }
+	{
+		if (object instanceof L2Vehicle)
+			return 8000;
+
+		final int knownlistSize = getKnownObjects().size();
+		if (knownlistSize <= 25)
+			return 3400; // empty field
+		if (knownlistSize <= 35)
+			return 2900;
+		if (knownlistSize <= 70)
+			return 2300;
+		else
+			return 1700; // Siege, TOI, city
+	}
 }

+ 40 - 0
L2_GameServer/java/com/l2jserver/gameserver/model/actor/stat/ControllableAirShipStat.java

@@ -0,0 +1,40 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.stat;
+
+import com.l2jserver.gameserver.model.actor.instance.L2ControllableAirShipInstance;
+
+public class ControllableAirShipStat extends VehicleStat
+{
+	public ControllableAirShipStat(L2ControllableAirShipInstance activeChar)
+	{
+		super(activeChar);
+	}
+
+	@Override
+	public L2ControllableAirShipInstance getActiveChar()
+	{
+		return (L2ControllableAirShipInstance)super.getActiveChar();
+	}
+
+	@Override
+	public float getMoveSpeed()
+	{
+		if (getActiveChar().isInDock() || getActiveChar().getFuel() > 0)
+			return super.getMoveSpeed();
+		else
+			return super.getMoveSpeed() * 0.05f;
+	}
+}

+ 1 - 1
L2_GameServer/java/com/l2jserver/gameserver/model/actor/stat/VehicleStat.java

@@ -27,7 +27,7 @@ public class VehicleStat extends CharStat
 	}
 
 	@Override
-	public final float getMoveSpeed()
+	public float getMoveSpeed()
 	{
 		return _moveSpeed;
 	}

+ 3 - 3
L2_GameServer/java/com/l2jserver/gameserver/network/L2GamePacketHandler.java

@@ -378,10 +378,10 @@ public final class L2GamePacketHandler implements IPacketHandler<L2GameClient>,
 						// RequestSEKCustom
 						break;
 					case 0x5b:
-						// StartRotating
+						msg = new StartRotating();
 						break;
 					case 0x5c:
-						// FinishRotating
+						msg = new FinishRotating();
 						break;
 					case 0x5e:
 						msg = new RequestShowBoard();
@@ -853,7 +853,7 @@ public final class L2GamePacketHandler implements IPacketHandler<L2GameClient>,
                                 msg = new ExGetOnAirShip();
                                 break;
                             case 0x38:
-                            	// MoveToLocationAirShip
+                            	msg = new MoveToLocationAirShip();
                             	break;
                             case 0x39:
                             	// RequestBidItemAuction

+ 94 - 4
L2_GameServer/java/com/l2jserver/gameserver/network/SystemMessageId.java

@@ -13981,13 +13981,85 @@ public enum SystemMessageId
 	 * Message: The cloak equip has been removed because the armor set equip has been removed.
 	 */
 	CLOAK_REMOVED_BECAUSE_ARMOR_SET_REMOVED(2451),
-	
+
+	/**
+	 * ID: 2455<br>
+	 * Message: The airship must be summoned in order for you to board.
+	 */
+	THE_AIRSHIP_MUST_BE_SUMMONED_TO_BOARD(2455),
+
+	/**
+	 * ID: 2456<br>
+	 * Message: In order to acquire an airship, the clan's level must be level 5 or higher.
+	 */
+	THE_AIRSHIP_NEED_CLANLVL_5_TO_SUMMON(2456),
+
+	/**
+	 * ID: 2457<br>
+	 * Message: An airship cannot be summoned because either you have not registered your airship license, or the airship has not yet been summoned
+	 */
+	THE_AIRSHIP_NEED_LICENSE_TO_SUMMON(2457),
+
+	/**
+	 * ID: 2458<br>
+	 * Message: The airship owned by the clan is already being used by another clan member.
+	 */
+	THE_AIRSHIP_ALREADY_USED(2458),
+
+	/**
+	 * ID: 2459<br>
+	 * Message: The Airship Summon License has already been acquired.
+	 */
+	THE_AIRSHIP_SUMMON_LICENSE_ALREADY_ACQUIRED(2459),
+
+	/**
+	 * ID:2460<br>
+	 * Message: The clan owned airship already exists.
+	 */
+	THE_AIRSHIP_IS_ALREADY_EXISTS(2460),
+
+	/**
+	 * ID:2461<br>
+	 * Message: The airship owned by the clan can only be purchased by the clan lord.
+	 */
+	THE_AIRSHIP_NO_PRIVILEGES(2461),
+
+	/**
+	 * ID:2462<br>
+	 * Message: The airship cannot be summoned because you don't have enough $s1%.
+	 */
+	THE_AIRSHIP_NEED_MORE_S1(2462),
+
+	/**
+	 * ID: 2463
+	 * Message: The airship's fuel (EP) will soon run out.
+	 */
+	THE_AIRSHIP_FUEL_SOON_RUN_OUT(2463),
+
+	/**
+	 * ID: 2464
+	 * Message: The airship's fuel (EP) has run out. The airship's speed will be greatly decreased in this condition.
+	 */
+	THE_AIRSHIP_FUEL_RUN_OUT(2464),
+
 	/**
 	 * ID: 2465
 	 * Message: You have selected a 3 vs 3 class irrelevant team match. Do you wish to participate?
 	 */
 	OLYMPIAD_3VS3_CONFIRM(2465),
-	
+
+	/**
+	 * ID: 2491
+	 * Message: Your ship cannot teleport because it does not have enough fuel for the trip.
+	 */
+	THE_AIRSHIP_CANNOT_TELEPORT(2491),
+
+	/**
+	 * ID: 2492
+	 * Message: The airship has been summoned. It will automatically depart in %s minutes.
+	 */
+	THE_AIRSHIP_SUMMONED(2492),
+
 	/**
 	* ID: 2500<br>
 	* Message: The collection has succeeded.
@@ -14029,7 +14101,13 @@ public enum SystemMessageId
 	* Message: Boarding or cancellation of boarding on Airships is not allowed in the current area.
 	*/
 	BOARD_OR_CANCEL_NOT_POSSIBLE_HERE(2721),
-	
+
+	/**
+	 * ID: 2722<br>
+	 * Message: Another airship has already been summoned at the wharf. Please try again later. 
+	 */
+	ANOTHER_AIRSHIP_ALREADY_SUMMONED(2722),
+
 	/**
 	* ID: 2727<br>
 	* Message: You cannot mount because you do not meet the requirements.
@@ -14047,7 +14125,13 @@ public enum SystemMessageId
 	* Message: The character that acquired $s1 ward has been killed.
 	*/
 	THE_CHAR_THAT_ACQUIRED_S1_WARD_HAS_BEEN_KILLED(2751),
-	
+
+	/**
+	 * ID: 2762<br>
+	 * Message: You cannot control because you are too far.
+	 */
+	CANT_CONTROL_TOO_FAR(2762),
+
 	/**
 	* ID: 2766<br>
 	* Message: Seed of Infinity Stage 1 Attack In Progress.
@@ -14096,6 +14180,12 @@ public enum SystemMessageId
 	*/
 	SEED_OF_DESTRUCTION_DEFENSE_IN_PROGRESS(2773),
 
+	/**
+	 * ID: 2777<br>
+	 * Message: The airship's summon license has been entered. Your clan can now summon the airship.
+	 */
+	THE_AIRSHIP_SUMMON_LICENSE_ENTERED(2777),
+
 	/**
 	* ID: 2795<br>
 	* Message: You've already requested a territory war in another territory elsewhere.

+ 3 - 0
L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/Action.java

@@ -87,6 +87,9 @@ public final class Action extends L2GameClientPacket
 			obj = activeChar.getTarget();
 			_removeSpawnProtection = true;
 		}
+		else if (activeChar.isInAirShip()
+				&& activeChar.getAirShip().getHelmObjectId() == _objectId)
+			obj = activeChar.getAirShip();
 		else
 			obj = L2World.getInstance().findObject(_objectId);
 

+ 16 - 3
L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/FinishRotating.java

@@ -14,6 +14,7 @@
  */
 package com.l2jserver.gameserver.network.clientpackets;
 
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.network.serverpackets.StopRotation;
 
 /**
@@ -39,10 +40,22 @@ public final class FinishRotating extends L2GameClientPacket
 	@Override
 	protected void runImpl()
 	{
-		if (getClient().getActiveChar() == null)
+		final L2PcInstance activeChar = getClient().getActiveChar();
+		if (activeChar == null)
 		    return;
-		StopRotation sr = new StopRotation(getClient().getActiveChar().getObjectId(), _degree, 0);
-		getClient().getActiveChar().broadcastPacket(sr);
+
+		StopRotation sr;
+		if (activeChar.isInAirShip() && activeChar.getAirShip().isCaptain(activeChar))
+		{
+			activeChar.getAirShip().setHeading(_degree);
+			sr = new StopRotation(activeChar.getAirShip().getObjectId(), _degree, 0);
+			activeChar.getAirShip().broadcastPacket(sr);
+		}
+		else
+		{
+			sr = new StopRotation(activeChar.getObjectId(), _degree, 0);
+			activeChar.broadcastPacket(sr);
+		}
 	}
 
 	/* (non-Javadoc)

+ 134 - 0
L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/MoveToLocationAirShip.java

@@ -0,0 +1,134 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.network.clientpackets;
+
+import com.l2jserver.gameserver.TaskPriority;
+import com.l2jserver.gameserver.ai.CtrlIntention;
+import com.l2jserver.gameserver.instancemanager.AirShipManager;
+import com.l2jserver.gameserver.model.L2CharPosition;
+import com.l2jserver.gameserver.model.L2World;
+import com.l2jserver.gameserver.model.VehiclePathPoint;
+import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.SystemMessageId;
+import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
+
+public class MoveToLocationAirShip extends L2GameClientPacket
+{
+	public static final int MIN_Z = -895;
+	public static final int MAX_Z = 6105;
+	public static final int STEP = 300;
+
+	private static final String _C__D0_38_MOVETOLOCATIONAIRSHIP = "[C] D0:38 MoveToLocationAirShip";
+
+	private int _command;
+	private int _param1;
+	private int _param2 = 0;
+
+	public TaskPriority getPriority()
+	{
+		return TaskPriority.PR_HIGH;
+	}
+
+	@Override
+	protected void readImpl()
+	{
+		_command = readD();
+		_param1 = readD();
+		if (_buf.remaining() > 0)
+			_param2 = readD();
+	}
+
+	@Override
+	protected void runImpl()
+	{
+		final L2PcInstance activeChar = getClient().getActiveChar();
+		if (activeChar == null)
+			return;
+
+		if (!activeChar.isInAirShip())
+			return;
+
+		final L2AirShipInstance ship = activeChar.getAirShip();
+		if (!ship.isCaptain(activeChar))
+			return;
+
+		int z = ship.getZ();
+
+		switch (_command)
+		{
+			case 0:
+				if (!ship.canBeControlled())
+					return;
+				if (_param1 < L2World.GRACIA_MAX_X)
+					ship.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(_param1, _param2, z, 0));
+				break;
+			case 1:
+				if (!ship.canBeControlled())
+					return;
+				ship.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
+				break;
+			case 2:
+				if (!ship.canBeControlled())
+					return;
+				if (z < L2World.GRACIA_MAX_Z)
+				{
+					z = Math.min(z + STEP, L2World.GRACIA_MAX_Z);
+					ship.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(ship.getX(), ship.getY(), z, 0));
+				}
+				break;
+			case 3:
+				if (!ship.canBeControlled())
+					return;
+				if (z > L2World.GRACIA_MIN_Z)
+				{
+					z = Math.max(z - STEP, L2World.GRACIA_MIN_Z);
+					ship.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(ship.getX(), ship.getY(), z, 0));
+				}
+				break;
+			case 4:
+				if (!ship.isInDock() || ship.isMoving())
+					return;
+
+				final VehiclePathPoint[] dst = AirShipManager.getInstance().getTeleportDestination(ship.getDockId(), _param1);
+				if (dst == null)
+					return;
+
+				// Consume fuel, if needed
+				final int fuelConsumption = AirShipManager.getInstance().getFuelConsumption(ship.getDockId(), _param1);
+				if (fuelConsumption > 0)
+				{
+					if (fuelConsumption > ship.getFuel())
+					{
+						activeChar.sendPacket(new SystemMessage(SystemMessageId.THE_AIRSHIP_CANNOT_TELEPORT));
+						return;
+					}
+					ship.setFuel(ship.getFuel() - fuelConsumption);
+				}
+
+				ship.executePath(dst);
+				break;
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see com.l2jserver.gameserver.clientpackets.ClientBasePacket#getType()
+	 */
+	@Override
+	public String getType()
+	{
+		return _C__D0_38_MOVETOLOCATIONAIRSHIP;
+	}
+}

+ 21 - 1
L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/RequestActionUse.java

@@ -22,6 +22,7 @@ import com.l2jserver.gameserver.ai.CtrlIntention;
 import com.l2jserver.gameserver.ai.L2SummonAI;
 import com.l2jserver.gameserver.datatables.PetSkillsTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.instancemanager.AirShipManager;
 import com.l2jserver.gameserver.instancemanager.CastleManager;
 import com.l2jserver.gameserver.instancemanager.TerritoryWarManager;
 import com.l2jserver.gameserver.model.L2CharPosition;
@@ -365,10 +366,29 @@ public final class RequestActionUse extends L2GameClientPacket
 				activeChar.sendMessage("Action not handled yet.");
 				break;
 			case 67: // Steer
+				if (activeChar.isInAirShip())
+					if (activeChar.getAirShip().setCaptain(activeChar))
+						activeChar.broadcastUserInfo();
+				break;
 			case 68: // Cancel Control
+				if (activeChar.isInAirShip() && activeChar.getAirShip().isCaptain(activeChar))
+					if (activeChar.getAirShip().setCaptain(null))
+						activeChar.broadcastUserInfo();
+				break;
 			case 69: // Destination Map
+				AirShipManager.getInstance().sendAirShipTeleportList(activeChar);
+				break;
 			case 70: // Exit Airship
-				activeChar.sendMessage("Action not handled yet.");
+				if (activeChar.isInAirShip())
+				{
+					if (activeChar.getAirShip().isCaptain(activeChar))
+					{
+						if (activeChar.getAirShip().setCaptain(null))
+							activeChar.broadcastUserInfo();
+					}
+					else if (activeChar.getAirShip().isInDock())
+						activeChar.getAirShip().oustPlayer(activeChar);
+				}
 				break;
 			case 1000: // Siege Golem - Siege Hammer
 				if (target instanceof L2DoorInstance)

+ 24 - 22
L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/RequestTargetCanceld.java

@@ -14,10 +14,10 @@
  */
 package com.l2jserver.gameserver.network.clientpackets;
 
-import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
+import com.l2jserver.gameserver.network.serverpackets.TargetUnselected;
 
 /**
  * This class ...
@@ -27,37 +27,39 @@ import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 public final class RequestTargetCanceld extends L2GameClientPacket
 {
 	private static final String _C__37_REQUESTTARGETCANCELD = "[C] 37 RequestTargetCanceld";
-	//private static Logger _log = Logger.getLogger(RequestTargetCanceld.class.getName());
 
-    private int _unselect;
+	private int _unselect;
 
 	@Override
 	protected void readImpl()
 	{
-        _unselect = readH();
+		_unselect = readH();
 	}
 
 	@Override
 	protected void runImpl()
 	{
-		L2Character activeChar = getClient().getActiveChar();
-        if (activeChar != null)
-        {
-        	if (((L2PcInstance)activeChar).isLockedTarget())
-        	{
-        		activeChar.sendPacket(new SystemMessage(SystemMessageId.FAILED_DISABLE_TARGET));
-        		return;
-        	}
-            if (_unselect == 0)
-            {
-            	if (activeChar.isCastingNow() && activeChar.canAbortCast())
-            		activeChar.abortCast();
-            	else if (activeChar.getTarget() != null)
-            		activeChar.setTarget(null);
-            }
-            else if (activeChar.getTarget() != null)
-            	activeChar.setTarget(null);
-        }
+		final L2PcInstance activeChar = getClient().getActiveChar();
+		if (activeChar == null)
+			return;
+
+		if (activeChar.isLockedTarget())
+		{
+			activeChar.sendPacket(new SystemMessage(SystemMessageId.FAILED_DISABLE_TARGET));
+			return;
+		}
+
+		if (_unselect == 0)
+		{
+			if (activeChar.isCastingNow() && activeChar.canAbortCast())
+				activeChar.abortCast();
+			else if (activeChar.getTarget() != null)
+				activeChar.setTarget(null);
+		}
+		else if (activeChar.getTarget() != null)
+			activeChar.setTarget(null);
+		else if (activeChar.isInAirShip())
+			activeChar.broadcastPacket(new TargetUnselected(activeChar));
 	}
 
 	/* (non-Javadoc)

+ 15 - 4
L2_GameServer/java/com/l2jserver/gameserver/network/clientpackets/StartRotating.java

@@ -14,6 +14,7 @@
  */
 package com.l2jserver.gameserver.network.clientpackets;
 
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.network.serverpackets.StartRotation;
 
 /**
@@ -28,7 +29,6 @@ public final class StartRotating extends L2GameClientPacket
 	private int _degree;
 	private int _side;
 
-
 	@Override
 	protected void readImpl()
 	{
@@ -39,10 +39,21 @@ public final class StartRotating extends L2GameClientPacket
 	@Override
 	protected void runImpl()
 	{
-		if (getClient().getActiveChar() == null)
+		final L2PcInstance activeChar = getClient().getActiveChar();
+		if (activeChar == null)
 		    return;
-		StartRotation br = new StartRotation(getClient().getActiveChar().getObjectId(), _degree, _side, 0);
-		getClient().getActiveChar().broadcastPacket(br);
+
+		final StartRotation br;
+		if (activeChar.isInAirShip() && activeChar.getAirShip().isCaptain(activeChar))
+		{
+			br = new StartRotation(activeChar.getAirShip().getObjectId(), _degree, _side, 0);
+			activeChar.getAirShip().broadcastPacket(br);
+		}
+		else
+		{
+			br = new StartRotation(activeChar.getObjectId(), _degree, _side, 0);
+			activeChar.broadcastPacket(br);
+		}
 	}
 
 	/* (non-Javadoc)

+ 43 - 9
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/CharInfo.java

@@ -71,6 +71,8 @@ public class CharInfo extends L2GameServerPacket
 	private int _territoryId;
 	private boolean _isDisguised;
 
+	private int _vehicleId, _airShipHelm;
+
 	/**
 	 * @param _characters
 	 */
@@ -78,9 +80,25 @@ public class CharInfo extends L2GameServerPacket
 	{
 		_activeChar = cha;
 		_inv = cha.getInventory();
-		_x = _activeChar.getX();
-		_y = _activeChar.getY();
-		_z = _activeChar.getZ();
+		if (_activeChar.getVehicle() != null && _activeChar.getInVehiclePosition() != null)
+		{
+			_x = _activeChar.getInVehiclePosition().getX();
+			_y = _activeChar.getInVehiclePosition().getY();
+			_z = _activeChar.getInVehiclePosition().getZ();
+			_vehicleId = _activeChar.getVehicle().getObjectId();
+			if (_activeChar.isInAirShip() && _activeChar.getAirShip().isCaptain(_activeChar))
+				_airShipHelm = _activeChar.getAirShip().getHelmItemId();
+			else
+				_airShipHelm = 0;
+		}
+		else
+		{
+			_x = _activeChar.getX();
+			_y = _activeChar.getY();
+			_z = _activeChar.getZ();
+			_vehicleId = 0;
+			_airShipHelm = 0;
+		}
 		_heading = _activeChar.getHeading();
 		_mAtkSpd = _activeChar.getMAtkSpd();
 		_pAtkSpd = _activeChar.getPAtkSpd();
@@ -202,7 +220,7 @@ public class CharInfo extends L2GameServerPacket
 			writeD(_x);
 			writeD(_y);
 			writeD(_z);
-			writeD(0x00);
+			writeD(_vehicleId);
 			writeD(_activeChar.getObjectId());
 			writeS(_activeChar.getAppearance().getVisibleName());
 			writeD(_activeChar.getRace().ordinal());
@@ -215,8 +233,16 @@ public class CharInfo extends L2GameServerPacket
 
 			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_UNDER));
 			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_HEAD));
-			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_RHAND));
-			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_LHAND));
+			if (_airShipHelm == 0)
+			{
+				writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_RHAND));
+				writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_LHAND));
+			}
+			else
+			{
+				writeD(_airShipHelm);
+				writeD(0);
+			}
 			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_GLOVES));
 			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_CHEST));
 			writeD(_inv.getPaperdollItemId(Inventory.PAPERDOLL_LEGS));
@@ -240,8 +266,16 @@ public class CharInfo extends L2GameServerPacket
 			// c6 new h's
 			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_UNDER));
 			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_HEAD));
-			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_RHAND));
-			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_LHAND));
+			if (_airShipHelm == 0)
+			{
+				writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_RHAND));
+				writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_LHAND));
+			}
+			else
+			{
+				writeD(0);
+				writeD(0);
+			}
 			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_GLOVES));
 			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_CHEST));
 			writeD(_inv.getPaperdollAugmentationId(Inventory.PAPERDOLL_LEGS));
@@ -370,7 +404,7 @@ public class CharInfo extends L2GameServerPacket
 
 			writeD(_activeChar.getClassId().getId());
 			writeD(0x00); //?
-			writeC(_activeChar.isMounted() ? 0 : _activeChar.getEnchantEffect());
+			writeC(_activeChar.isMounted() || _airShipHelm != 0 ? 0 : _activeChar.getEnchantEffect());
 
 			if(_activeChar.getTeam()==1)
 				writeC(0x01); //team circle around feet 1= Blue, 2 = red

+ 6 - 2
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/DeleteObject.java

@@ -27,13 +27,18 @@ import com.l2jserver.gameserver.model.L2Object;
 public final class DeleteObject extends L2GameServerPacket
 {
 	private static final String _S__1E_DELETEOBJECT = "[S] 08 DeleteObject";
-	private int _objectId;
+	private final int _objectId;
 
 	public DeleteObject(L2Object obj)
 	{
 		_objectId = obj.getObjectId();
 	}
 
+	public DeleteObject(int objectId)
+	{
+		_objectId = objectId;
+	}
+
 	@Override
 	protected final void writeImpl()
 	{
@@ -50,5 +55,4 @@ public final class DeleteObject extends L2GameServerPacket
 	{
 		return _S__1E_DELETEOBJECT;
 	}
-
 }

+ 52 - 41
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExAirShipInfo.java

@@ -17,57 +17,68 @@ package com.l2jserver.gameserver.network.serverpackets;
 import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance;
 
 public class ExAirShipInfo extends L2GameServerPacket
-{
-	
+{	
 	private static final String _S__FE_60_EXAIRSHIPINFO = "[S] FE:60 ExAirShipInfo";
-	
-	private final int _x, _y, _z, _heading, _objectId, _speed1, _speed2;
-	
-	
+
+	// store some parameters, because they can be changed during broadcast
+	private final L2AirShipInstance _ship;
+	private final int _x, _y, _z, _heading, _moveSpeed, _rotationSpeed, _captain, _helm;
+
 	public ExAirShipInfo(L2AirShipInstance ship)
 	{
+		_ship = ship;
 		_x = ship.getX();
 		_y = ship.getY();
 		_z = ship.getZ();
-		_heading = ship.getPosition().getHeading();
-		_objectId = ship.getObjectId();
-		_speed1 = (int)ship.getStat().getMoveSpeed();
-		_speed2 = ship.getStat().getRotationSpeed();
+		_heading = ship.getHeading();
+		_moveSpeed = (int)ship.getStat().getMoveSpeed();
+		_rotationSpeed = ship.getStat().getRotationSpeed();
+		_captain = ship.getCaptainId();
+		_helm = ship.getHelmObjectId();
 	}
 
 	@Override
-    protected void writeImpl()
-    {
-	    writeC(0xfe);
-	    writeH(0x60);
-	    
-	    writeD(_objectId);
-	    writeD(_x);
-	    writeD(_y);
-	    writeD(_z);
-	    writeD(_heading);
-	    
-	    
-	    writeD(0x00); // object id of player who control ship
-	    writeD(_speed1);
-	    writeD(_speed2);
-	    
-	    // clan airship related info
-	    writeD(0x00); // owner object id?
-	    writeD(0x00); // 366?
-	    writeD(0x00); // 0
-	    writeD(0x00); // 107:
-	    writeD(0x00); // 348?
-	    writeD(0x00); // 0?
-	    writeD(0x00); // 105?
-	    writeD(0x00); // current fuel
-	    writeD(0x00); // max fuel
+	protected void writeImpl()
+	{
+		writeC(0xfe);
+		writeH(0x60);
+
+		writeD(_ship.getObjectId());
+		writeD(_x);
+		writeD(_y);
+		writeD(_z);
+		writeD(_heading);
+
+		writeD(_captain);
+		writeD(_moveSpeed);
+		writeD(_rotationSpeed);
+		writeD(_helm);
+		if (_helm != 0)
+		{
+			writeD(0x16e); // Controller X
+			writeD(0x00); // Controller Y
+			writeD(0x6b); // Controller Z
+			writeD(0x15c); // Captain X
+			writeD(0x00); // Captain Y
+			writeD(0x69); // Captain Z
+		}
+		else
+		{
+			writeD(0x00);
+			writeD(0x00);
+			writeD(0x00);
+			writeD(0x00);
+			writeD(0x00);
+			writeD(0x00);
+		}
+
+		writeD(_ship.getFuel());
+		writeD(_ship.getMaxFuel());
 	}
 
 	@Override
-    public String getType()
-    {
-	    return _S__FE_60_EXAIRSHIPINFO;
-    }
-	
+	public String getType()
+	{
+		return _S__FE_60_EXAIRSHIPINFO;
+	}
 }

+ 67 - 0
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExAirShipTeleportList.java

@@ -0,0 +1,67 @@
+/*
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.network.serverpackets;
+
+import com.l2jserver.gameserver.model.VehiclePathPoint;
+
+public class ExAirShipTeleportList extends L2GameServerPacket
+{
+	private static final String _S__FE_9A_EXAIRSHIPTELEPORTLIST = "[S] FE:9A ExAirShipTeleportList";
+
+	private int _dockId;
+	private VehiclePathPoint[][] _teleports;
+	private int[] _fuelConsumption;
+
+	public ExAirShipTeleportList(int dockId, VehiclePathPoint[][] teleports, int[] fuelConsumption)
+	{
+		_dockId = dockId;
+		_teleports = teleports;
+		_fuelConsumption = fuelConsumption;
+	}
+
+	@Override
+	protected void writeImpl()
+	{
+		writeC(0xfe);
+		writeH(0x9a);
+
+		writeD(_dockId);
+		if (_teleports != null)
+		{
+			writeD(_teleports.length);
+
+			VehiclePathPoint[] path;
+			VehiclePathPoint dst;
+			for (int i = 0; i < _teleports.length; i++)
+			{
+				writeD(i - 1);
+				writeD(_fuelConsumption[i]);
+				path = _teleports[i];
+				dst = path[path.length - 1];
+				writeD(dst.x);
+				writeD(dst.y);
+				writeD(dst.z);
+			}
+		}
+		else
+			writeD(0);
+	}
+
+	@Override
+	public String getType()
+	{
+		return _S__FE_9A_EXAIRSHIPTELEPORTLIST;
+	}
+}

+ 15 - 9
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExMoveToLocationAirShip.java

@@ -20,11 +20,17 @@ public class ExMoveToLocationAirShip extends L2GameServerPacket
 {	
 	private static final String _S__FE_65_EXAIRSHIPMOVETOLOCATION = "[S] FE:65 ExMoveToLocationAirShip";
 
-	private L2Character _ship;
+	private final int _objId, _tx, _ty, _tz, _x, _y, _z;
 
 	public ExMoveToLocationAirShip(L2Character cha)
 	{
-		_ship = cha;
+		_objId = cha.getObjectId();
+		_tx = cha.getXdestination();
+		_ty = cha.getYdestination();
+		_tz = cha.getZdestination();
+		_x = cha.getX();
+		_y = cha.getY();
+		_z = cha.getZ();
 	}
 
 	@Override
@@ -33,13 +39,13 @@ public class ExMoveToLocationAirShip extends L2GameServerPacket
 		writeC(0xfe);
 		writeH(0x65);
 
-		writeD(_ship.getObjectId());
-		writeD(_ship.getXdestination());
-		writeD(_ship.getYdestination());
-		writeD(_ship.getZdestination());
-		writeD(_ship.getX());
-		writeD(_ship.getY());
-		writeD(_ship.getZ());
+		writeD(_objId);
+		writeD(_tx);
+		writeD(_ty);
+		writeD(_tz);
+		writeD(_x);
+		writeD(_y);
+		writeD(_z);
 	}
 
 	@Override

+ 13 - 8
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/ExStopMoveAirShip.java

@@ -22,11 +22,16 @@ import com.l2jserver.gameserver.model.actor.L2Character;
  */
 public class ExStopMoveAirShip extends L2GameServerPacket
 {
-	private L2Character _ship;
+	// store coords here because they can be changed from other threads
+	final int _objectId, _x, _y, _z, _heading;
 
-    public ExStopMoveAirShip(L2Character ship)
+	public ExStopMoveAirShip(L2Character ship)
     {
-    	_ship = ship;
+		_objectId = ship.getObjectId();
+		_x = ship.getX();
+		_y = ship.getY();
+		_z = ship.getZ();
+		_heading = ship.getHeading();
     }
 
     @Override
@@ -34,11 +39,11 @@ public class ExStopMoveAirShip extends L2GameServerPacket
     {
         writeC(0xfe);
         writeH(0x66);
-        writeD(_ship.getObjectId());
-        writeD(_ship.getX());
-        writeD(_ship.getY());
-        writeD(_ship.getZ());
-        writeD(_ship.getHeading());
+        writeD(_objectId);
+        writeD(_x);
+        writeD(_y);
+        writeD(_z);
+        writeD(_heading);
     }
 
     /* (non-Javadoc)

+ 39 - 15
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/UserInfo.java

@@ -82,7 +82,7 @@ public final class UserInfo extends L2GameServerPacket
 	private float _moveMultiplier;
 	private int _territoryId;
 	private boolean _isDisguised;
-	private int _vehicleObjectId;
+	private int _airShipHelm;
 	
 	/**
 	 * @param _characters
@@ -105,13 +105,10 @@ public final class UserInfo extends L2GameServerPacket
 		}
 		if (_activeChar.getSiegeState() == 2) _relation |= 0x80;
 		_isDisguised = TerritoryWarManager.getInstance().isDisguised(character.getObjectId());
-		if (_activeChar.isInBoat())
-			_vehicleObjectId = _activeChar.getBoat().getObjectId();
-		else if (_activeChar.isInAirShip())
-			_vehicleObjectId = _activeChar.getAirShip().getObjectId();
+		if (_activeChar.isInAirShip() && _activeChar.getAirShip().isCaptain(_activeChar))
+			_airShipHelm = _activeChar.getAirShip().getHelmItemId();
 		else
-			_vehicleObjectId = 0;
-		
+			_airShipHelm = 0;
 	}
 	
 	@Override
@@ -122,7 +119,10 @@ public final class UserInfo extends L2GameServerPacket
 		writeD(_activeChar.getX());
 		writeD(_activeChar.getY());
 		writeD(_activeChar.getZ());
-		writeD(_vehicleObjectId); // heading from CT2.3 no longer used inside userinfo, here is now vehicle id (boat,airship)
+		if (_activeChar.getVehicle() != null)
+			writeD(_activeChar.getVehicle().getObjectId());
+		else
+			writeD(0);
 		writeD(_activeChar.getObjectId());
 		writeS(_activeChar.getName());
 		writeD(_activeChar.getRace().ordinal());
@@ -156,8 +156,16 @@ public final class UserInfo extends L2GameServerPacket
 		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_RFINGER));
 		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LFINGER));
 		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_HEAD));
-		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_RHAND));
-		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LHAND));
+		if (_airShipHelm == 0)
+		{
+			writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_RHAND));
+			writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LHAND));
+		}
+		else
+		{
+			writeD(0);
+			writeD(0);
+		}
 		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_GLOVES));
 		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_CHEST));
 		writeD(_activeChar.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LEGS));
@@ -182,8 +190,16 @@ public final class UserInfo extends L2GameServerPacket
 		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_RFINGER));
 		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_LFINGER));
 		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_HEAD));
-		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_RHAND));
-		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_LHAND));
+		if (_airShipHelm == 0)
+		{
+			writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_RHAND));
+			writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_LHAND));
+		}
+		else
+		{
+			writeD(_airShipHelm);
+			writeD(0);
+		}
 		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_GLOVES));
 		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_CHEST));
 		writeD(_activeChar.getInventory().getPaperdollItemId(Inventory.PAPERDOLL_LEGS));
@@ -208,8 +224,16 @@ public final class UserInfo extends L2GameServerPacket
 		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_RFINGER));
 		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_LFINGER));
 		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_HEAD));
-		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_RHAND));
-		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_LHAND));
+		if (_airShipHelm == 0)
+		{
+			writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_RHAND));
+			writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_LHAND));
+		}
+		else
+		{
+			writeD(0);
+			writeD(0);
+		}
 		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_GLOVES));
 		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_CHEST));
 		writeD(_activeChar.getInventory().getPaperdollAugmentationId(Inventory.PAPERDOLL_LEGS));
@@ -325,7 +349,7 @@ public final class UserInfo extends L2GameServerPacket
 		writeD(0x00); // special effects? circles around player...
 		writeD(_activeChar.getMaxCp());
 		writeD((int) _activeChar.getCurrentCp());
-		writeC(_activeChar.isMounted() ? 0 : _activeChar.getEnchantEffect());
+		writeC(_activeChar.isMounted() || _airShipHelm != 0 ? 0 : _activeChar.getEnchantEffect());
 		
 		if(_activeChar.getTeam()==1)
 			writeC(0x01); //team circle around feet 1= Blue, 2 = red

+ 14 - 13
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/VehicleDeparture.java

@@ -22,19 +22,20 @@ import com.l2jserver.gameserver.model.actor.instance.L2BoatInstance;
  */
 public class VehicleDeparture extends L2GameServerPacket
 {
-	private L2BoatInstance _boat;
+	// Store parameters because they can be changed during broadcast
+	private final int _objId, _x, _y, _z, _moveSpeed, _rotationSpeed;
 
 	/**
 	 * @param _boat
-	 * @param speed1
-	 * @param speed2
-	 * @param x
-	 * @param y
-	 * @param z
 	 */
 	public VehicleDeparture(L2BoatInstance boat)
 	{
-		_boat = boat;
+		_objId = boat.getObjectId();
+		_x = boat.getXdestination();
+		_y = boat.getYdestination();
+		_z = boat.getZdestination();
+		_moveSpeed = (int)boat.getStat().getMoveSpeed();
+		_rotationSpeed = boat.getStat().getRotationSpeed();
 	}
 
 	@Override
@@ -42,12 +43,12 @@ public class VehicleDeparture extends L2GameServerPacket
 	void writeImpl()
 	{
 		writeC(0x6c);
-		writeD(_boat.getObjectId());
-		writeD((int)_boat.getStat().getMoveSpeed());
-		writeD(_boat.getStat().getRotationSpeed());
-		writeD(_boat.getXdestination());
-		writeD(_boat.getYdestination());
-		writeD(_boat.getZdestination());
+		writeD(_objId);
+		writeD(_moveSpeed);
+		writeD(_rotationSpeed);
+		writeD(_x);
+		writeD(_y);
+		writeD(_z);
 
 	}
 

+ 13 - 10
L2_GameServer/java/com/l2jserver/gameserver/network/serverpackets/VehicleInfo.java

@@ -22,13 +22,16 @@ import com.l2jserver.gameserver.model.actor.instance.L2BoatInstance;
  */
 public class VehicleInfo  extends L2GameServerPacket
 {
-	 private L2BoatInstance _boat;
-	/**
-	 * @param instance
-	 */
+	// Store some parameters here because they can be changed during broadcast
+	private final int _objId, _x, _y, _z, _heading;
+
 	public VehicleInfo(L2BoatInstance boat)
 	{
-		_boat = boat;
+		_objId = boat.getObjectId();
+		_x = boat.getX();
+		_y = boat.getY();
+		_z = boat.getZ();
+		_heading = boat.getHeading();
 	}
 
 	/* (non-Javadoc)
@@ -38,11 +41,11 @@ public class VehicleInfo  extends L2GameServerPacket
 	protected void writeImpl()
 	{
 		writeC(0x60);
-		writeD(_boat.getObjectId());
-		writeD(_boat.getX());
-		writeD(_boat.getY());
-        writeD(_boat.getZ());
-        writeD(_boat.getHeading());
+		writeD(_objId);
+		writeD(_x);
+		writeD(_y);
+        writeD(_z);
+        writeD(_heading);
 	}
 
 	/* (non-Javadoc)

+ 4 - 1
L2_GameServer/java/com/l2jserver/gameserver/templates/skills/L2SkillType.java

@@ -192,7 +192,10 @@ public enum L2SkillType
 	COREDONE,
 	
 	CHANGE_APPEARANCE(L2SkillAppearance.class),
-	
+
+	// Refuel airship
+	REFUEL,
+
 	// unimplemented
 	NOTDONE, BALLISTA;