/*
* Copyright (C) 2004-2015 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import com.l2jserver.Config;
import com.l2jserver.gameserver.GameTimeController;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.enums.InstanceType;
import com.l2jserver.gameserver.instancemanager.MapRegionManager;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.L2WorldRegion;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.TeleportWhereType;
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.model.actor.templates.L2CharTemplate;
import com.l2jserver.gameserver.model.interfaces.ILocational;
import com.l2jserver.gameserver.model.items.L2Weapon;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.gameserver.util.Util;
/**
* @author DS
*/
public abstract class L2Vehicle extends L2Character
{
protected int _dockId = 0;
protected final ArrayList<L2PcInstance> _passengers = new ArrayList<>();
protected Location _oustLoc = null;
private Runnable _engine = null;
protected VehiclePathPoint[] _currentPath = null;
protected int _runState = 0;
/**
* Creates an abstract vehicle.
* @param template the vehicle template
*/
public L2Vehicle(L2CharTemplate template)
{
super(template);
setInstanceType(InstanceType.L2Vehicle);
setIsFlying(true);
}
public boolean isBoat()
{
return false;
}
public boolean isAirShip()
{
return false;
}
public boolean canBeControlled()
{
return _engine == null;
}
public void registerEngine(Runnable r)
{
_engine = r;
}
public void runEngine(int delay)
{
if (_engine != null)
{
ThreadPoolManager.getInstance().scheduleGeneral(_engine, delay);
}
}
public void executePath(VehiclePathPoint[] path)
{
_runState = 0;
_currentPath = path;
if ((_currentPath != null) && (_currentPath.length > 0))
{
final VehiclePathPoint point = _currentPath[0];
if (point.getMoveSpeed() > 0)
{
getStat().setMoveSpeed(point.getMoveSpeed());
}
if (point.getRotationSpeed() > 0)
{
getStat().setRotationSpeed(point.getRotationSpeed());
}
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(point.getX(), point.getY(), point.getZ(), 0));
return;
}
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
}
@Override
public boolean moveToNextRoutePoint()
{
_move = null;
if (_currentPath != null)
{
_runState++;
if (_runState < _currentPath.length)
{
final VehiclePathPoint point = _currentPath[_runState];
if (!isMovementDisabled())
{
if (point.getMoveSpeed() == 0)
{
point.setHeading(point.getRotationSpeed());
teleToLocation(point, false);
_currentPath = null;
}
else
{
if (point.getMoveSpeed() > 0)
{
getStat().setMoveSpeed(point.getMoveSpeed());
}
if (point.getRotationSpeed() > 0)
{
getStat().setRotationSpeed(point.getRotationSpeed());
}
MoveData m = new MoveData();
m.disregardingGeodata = false;
m.onGeodataPathIndex = -1;
m._xDestination = point.getX();
m._yDestination = point.getY();
m._zDestination = point.getZ();
m._heading = 0;
final double dx = point.getX() - getX();
final double dy = point.getY() - getY();
final double distance = Math.sqrt((dx * dx) + (dy * dy));
if (distance > 1)
{
setHeading(Util.calculateHeadingFrom(getX(), getY(), point.getX(), point.getY()));
}
m._moveStartTime = GameTimeController.getInstance().getGameTicks();
_move = m;
GameTimeController.getInstance().registerMovingObject(this);
return true;
}
}
}
else
{
_currentPath = null;
}
}
runEngine(10);
return false;
}
@Override
public void initKnownList()
{
setKnownList(new VehicleKnownList(this));
}
@Override
public VehicleStat getStat()
{
return (VehicleStat) super.getStat();
}
@Override
public void initCharStat()
{
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;
}
public Location getOustLoc()
{
return _oustLoc != null ? _oustLoc : MapRegionManager.getInstance().getTeleToLocation(this, TeleportWhereType.TOWN);
}
public void oustPlayers()
{
L2PcInstance player;
// Use iterator because oustPlayer will try to remove player from _passengers
final Iterator<L2PcInstance> iter = _passengers.iterator();
while (iter.hasNext())
{
player = iter.next();
iter.remove();
if (player != null)
{
oustPlayer(player);
}
}
}
public void oustPlayer(L2PcInstance player)
{
player.setVehicle(null);
player.setInVehiclePosition(null);
removePassenger(player);
}
public boolean addPassenger(L2PcInstance player)
{
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;
}
public void removePassenger(L2PcInstance player)
{
try
{
_passengers.remove(player);
}
catch (Exception e)
{
}
}
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
* @param count Ticket count
* @param oustX
* @param oustY
* @param oustZ
*/
public void payForRide(int itemId, int count, int oustX, int oustY, int oustZ)
{
final Collection<L2PcInstance> passengers = getKnownList().getKnownPlayersInRadius(1000);
if ((passengers != null) && !passengers.isEmpty())
{
L2ItemInstance ticket;
InventoryUpdate iu;
for (L2PcInstance player : passengers)
{
if (player == null)
{
continue;
}
if (player.isInBoat() && (player.getBoat() == this))
{
if (itemId > 0)
{
ticket = player.getInventory().getItemByItemId(itemId);
if ((ticket == null) || (player.getInventory().destroyItem("Boat", ticket, count, player, this) == null))
{
player.sendPacket(SystemMessageId.YOU_DO_NOT_POSSESS_THE_CORRECT_TICKET_TO_BOARD_THE_BOAT);
player.teleToLocation(new Location(oustX, oustY, oustZ), true);
continue;
}
iu = new InventoryUpdate();
iu.addModifiedItem(ticket);
player.sendPacket(iu);
}
addPassenger(player);
}
}
}
}
@Override
public boolean updatePosition()
{
final boolean result = super.updatePosition();
for (L2PcInstance player : _passengers)
{
if ((player != null) && (player.getVehicle() == this))
{
player.setXYZ(getX(), getY(), getZ());
player.revalidateZone(false);
}
}
return result;
}
@Override
public void teleToLocation(ILocational loc, boolean allowRandomOffset)
{
if (isMoving())
{
stopMove(null, false);
}
setIsTeleporting(true);
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
for (L2PcInstance player : _passengers)
{
if (player != null)
{
player.teleToLocation(loc, false);
}
}
decayMe();
setXYZ(loc.getX(), loc.getY(), loc.getZ());
// temporary fix for heading on teleports
if (loc.getHeading() != 0)
{
setHeading(loc.getHeading());
}
onTeleported();
revalidateZone(true);
}
@Override
public void stopMove(Location loc, boolean updateKnownObjects)
{
_move = null;
if (loc != null)
{
setXYZ(loc.getX(), loc.getY(), loc.getZ());
setHeading(loc.getHeading());
revalidateZone(true);
}
if (Config.MOVE_BASED_KNOWNLIST && updateKnownObjects)
{
getKnownList().findObjects();
}
}
@Override
public boolean 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);
return super.deleteMe();
}
@Override
public void updateAbnormalVisualEffects()
{
}
@Override
public L2ItemInstance getActiveWeaponInstance()
{
return null;
}
@Override
public L2Weapon getActiveWeaponItem()
{
return null;
}
@Override
public L2ItemInstance getSecondaryWeaponInstance()
{
return null;
}
@Override
public L2Weapon getSecondaryWeaponItem()
{
return null;
}
@Override
public int getLevel()
{
return 0;
}
@Override
public boolean isAutoAttackable(L2Character attacker)
{
return false;
}
@Override
public boolean isVehicle()
{
return true;
}
}