/* * 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 . */ 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; import com.l2jserver.Config; import com.l2jserver.gameserver.GameTimeController; import com.l2jserver.gameserver.ThreadPoolManager; import com.l2jserver.gameserver.ai.CtrlIntention; 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.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; import com.l2jserver.gameserver.util.Util; /** * * @author DS * */ public abstract class L2Vehicle extends L2Character { protected int _dockId = 0; protected final FastList _passengers = new FastList(); 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); } 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.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; } 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.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); 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 : MapRegionTable.getInstance().getTeleToLocation(this, MapRegionTable.TeleportWhereType.Town); } public void oustPlayers() { L2PcInstance player; // Use iterator because oustPlayer will try to remove player from _passengers final Iterator 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 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 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(SystemMessage.getSystemMessage(SystemMessageId.NOT_CORRECT_BOAT_TICKET)); player.teleToLocation(oustX, oustY, oustZ, true); continue; } iu = new InventoryUpdate(); iu.addModifiedItem(ticket); player.sendPacket(iu); } addPassenger(player); } } } } @Override public boolean updatePosition(int gameTicks) { final boolean result = super.updatePosition(gameTicks); for (L2PcInstance player : _passengers) { if (player != null && player.getVehicle() == this) { 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) { if (isMoving()) stopMove(null, false); setIsTeleporting(true); getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE); for (L2PcInstance player : _passengers) { if (player != null) player.teleToLocation(x, y, z); } decayMe(); setXYZ(x, y, z); // temporary fix for heading on teleports if (heading != 0) getPosition().setHeading(heading); onTeleported(); revalidateZone(true); } @Override public void stopMove(L2CharPosition pos, boolean updateKnownObjects) { _move = null; if (pos != null) { setXYZ(pos.x, pos.y, pos.z); setHeading(pos.heading); revalidateZone(true); } if (Config.MOVE_BASED_KNOWNLIST && updateKnownObjects) 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() { } @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 void setAI(L2CharacterAI newAI) { if (_ai == null) _ai = newAI; } public class AIAccessor extends L2Character.AIAccessor { @Override public void detachAI() {} } }