Jump to content

Question

Posted (edited)

Hi there. How to inject this movement contoller into aCis latest source?
 

package ru.catssoftware.gameserver.model.actor;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import ru.catssoftware.gameserver.ThreadPoolManager;
import ru.catssoftware.gameserver.ai.CtrlEvent;
import ru.catssoftware.gameserver.ai.CtrlIntention;
import ru.catssoftware.gameserver.ai.ParallelManager;
import ru.catssoftware.gameserver.geodata.GeoData;
import ru.catssoftware.gameserver.geodata.MathUtil;
import ru.catssoftware.gameserver.geodata.pathfinding.AbstractNodeLoc;
import ru.catssoftware.gameserver.geodata.pathfinding.PathFinding;
import ru.catssoftware.gameserver.idfactory.IdFactory;
import ru.catssoftware.gameserver.model.L2Character;
import ru.catssoftware.gameserver.model.L2ItemInstance;
import ru.catssoftware.gameserver.model.Location;
import ru.catssoftware.gameserver.model.MoveModel;
import ru.catssoftware.gameserver.network.serverpackets.MoveToLocation;
import ru.catssoftware.gameserver.util.Util;

import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class MovementController
{
	private final static int MAX_DISTANCE = 5000;
	private final static int PAWN_OFFSET = 55;

	private final ReentrantLock lock = new ReentrantLock(true);
	private final L2Character actor;
	private ScheduledFuture<?> watchTask;
	private ScheduledFuture<?> pawnWatcher;

	private final AtomicReference<MoveModel> moveModel = new AtomicReference<>();
	private List<AbstractNodeLoc> path;
	private Location originalEndPoint;

	@Getter private L2Character pawnTarget;
	private int pawnOffset;

	private long initTime;

	public MovementController(L2Character actor)
	{
		this.actor = actor;
	}

	public boolean isMoving()
	{
		return moveModel.get() != null;
	}

	public boolean isPawnMoving()
	{
		return pawnTarget != null || (pawnWatcher != null && !pawnWatcher.isDone());
	}

	public Location getDestiny()
	{
		MoveModel model = moveModel.get();
		if(model == null)
			return actor.getLoc();
		return model.getEndMovePoint();
	}

	public void stopMove()
	{
		stopWatcher();
		stopPawnWatcher();
		moveModel.set(null);
		originalEndPoint = null;
		pawnTarget = null;
		path = null;
	}

	public void movePawn(L2Character pawn, int offset)
	{
		if (true)
		{
			move(pawn.getX(), pawn.getY(), pawn.getZ(), offset, true, -1);
			return;
		}
		Location current = actor.getLoc();
		Location dest = pawn.getLoc();

		if (offset == 0)
			pawnOffset = PAWN_OFFSET;
		else
			pawnOffset = offset;

		double distance = current.getDistance(dest);

		//debug("movePawn: " + distance + " <= " + (pawnOffset) + " = " + (distance <= pawnOffset));

		if(distance <= pawnOffset)
		{
			startPawnWatcher(pawn);
			ParallelManager.getInstance().notify(actor.getAI(), CtrlEvent.EVT_ARRIVED);
			return;
		}

		if(distance > MAX_DISTANCE)
		{
			stopMove();
			ParallelManager.getInstance().intention(actor.getAI(), CtrlIntention.AI_INTENTION_ACTIVE);
			return;
		}

		initTime = System.currentTimeMillis();

		movePawn0(pawn, current, dest);
	}

	private void movePawn0(L2Character pawn, Location current, Location dest) {
		lock.lock();
		try
		{
			stopMove();

			dest = normalizeByOffset(dest, pawnOffset);
			dest = checkWay(current, dest);

			actor.getListeners().onMove(actor, dest);

			MoveModel model = new MoveModel();
			model.setWithoutGeodata(false);
			model.setStartMovePoint(current);
			model.setEndMovePoint(dest);
			model.setDistance(current.getDistance(dest));
			moveModel.set(model);

			validateHeading(current, dest);
			actor.broadcastPacket(new MoveToLocation(actor, dest));

			pawnTarget = pawn;
			startWatcher(model);
			startPawnWatcher(pawn);
		}
		finally
		{
			lock.unlock();
		}
	}

	private void stopWatcher() {
		if(watchTask != null && !watchTask.isDone())
			watchTask.cancel(false);
	}

	private void startWatcher(MoveModel model)
	{
		if(model != null)
			model.setPrevUpdateTime(System.currentTimeMillis());

		watchTask = ThreadPoolManager.getInstance().scheduleMove(new RunnableWatch(), getTime(actor));
	}

	private void watch()
	{
		if (actor.isMovementDisabled())
		{
			stopMove();
			return;
		}

		if (initTime + 120000 < System.currentTimeMillis())
		{
			stopMove();
			return;
		}

		MoveModel moveModel = this.moveModel.get();

		if (moveModel == null)
		{
			stopWatcher();
			stopMove();
			return;
		}

		long timeNow = System.currentTimeMillis();

		double moveSpeed = actor.getStat().getMoveSpeed();
		double complete = (moveSpeed / 1000d) * (timeNow - moveModel.getPrevUpdateTime());
		double divider = (complete + moveModel.getCompleteDistance()) / moveModel.getDistance();
		moveModel.setPrevUpdateTime(timeNow);
		moveModel.setCompleteDistance(complete + moveModel.getCompleteDistance());

		if (divider >= 1)
		{
			actor.setXYZ(moveModel.getEndMovePoint());
			completeMove(moveModel);
			return;
		}

		int dx = moveModel.getEndMovePoint().getX() - moveModel.getStartMovePoint().getX();
		int dy = moveModel.getEndMovePoint().getY() - moveModel.getStartMovePoint().getY();
		int dz = moveModel.getEndMovePoint().getZ() - moveModel.getStartMovePoint().getZ();

		Location start = moveModel.getStartMovePoint();
		int x = start.getX() + (int) Math.round(divider * dx);
		int y = start.getY() + (int) Math.round(divider * dy);
		int z;
		if (GeoData.getInstance().isEnabled() && moveModel.isWithoutGeodata())
			z = start.getZ() + (int) Math.round(divider * dz);
		else
			z = GeoData.getInstance().getMoveHeight(x, y, actor.getZ());

		if (!this.moveModel.compareAndSet(moveModel, moveModel))
			return;

		dropItem(x, y, z, 57);

		actor.setXYZ(x, y, z);

		startWatcher(moveModel);
		actor.revalidateZoneMT(false);
	}


	private void startPawnWatcher(final L2Character pawn)
	{
		stopPawnWatcher();
		pawnWatcher = ThreadPoolManager.getInstance().scheduleMove(new RunnableWatchPawn(pawn), getTime(pawn) << 1);
	}

	private void stopPawnWatcher()
	{
		if(pawnWatcher != null && !pawnWatcher.isDone())
			pawnWatcher.cancel(false);
	}

	private void watchPawn(L2Character pawn)
	{
		if (actor.isMovementDisabled())
		{
			stopMove();
			return;
		}

		if(isMoving())
		{
			startPawnWatcher(pawn);
			return;
		}

		//if (initTime + 120000 < System.currentTimeMillis())
			//log.info("{}: watchPawn({}) very long ~ {} ms. {} -> {}", actor, pawn, System.currentTimeMillis() - initTime, actor.getLoc(), getDestiny());

		Location current = actor.getLoc();
		Location dest = pawn.getLoc();

		double distance = current.getDistance(dest);
		//debug("watchPawn: " + distance + " <= " + (pawnOffset) + " = " + (distance <= pawnOffset));
		if(distance <= pawnOffset)
		{
			startPawnWatcher(pawn);
			return;
		}

		if(distance > MAX_DISTANCE)
		{
			ParallelManager.getInstance().intention(actor.getAI(), CtrlIntention.AI_INTENTION_ACTIVE);
			return;
		}

		movePawn0(pawn, current, dest);
	}

	public void moveWoGeodata(int x, int y, int z, int offset, boolean broadcast) {
		lock.lock();
		try
		{
			stopMove();

			Location dest;

			if(offset != 0)
				dest = normalizeByOffset(new Location(x, y, z), offset);
			else
				dest = new Location(x, y, z);

			Location current = actor.getLoc();

			actor.getListeners().onMove(actor, dest);

			MoveModel model = new MoveModel();
			model.setWithoutGeodata(false);
			model.setStartMovePoint(current);
			model.setEndMovePoint(dest);
			model.setDistance(current.getDistance(dest));
			model.setWithoutGeodata(true);
			moveModel.set(model);
			if(broadcast)
				actor.broadcastPacket(new MoveToLocation(actor, dest));

			initTime = System.currentTimeMillis();

			startWatcher(model);
		} finally {
			lock.unlock();
		}
	}

	public void moveWoPathfind(int x, int y, int z, int offset, boolean broadcast, int doorid) {
		lock.lock();
		try {
			stopMove();

			Location dest;
			if(offset != 0)
				dest = normalizeByOffset(new Location(x, y, z), offset);
			else
				dest = new Location(x, y, z);

			Location current = actor.getLoc();
			double distance2 = current.getDistance2D(dest);
			if(actor.hasDebuger())
				actor.say("Distance: {}", distance2);

			Location geoDest = cutByGeodata(dest, doorid);

			double destgeoDistance = dest.getDistance2D(geoDest);
			if(destgeoDistance > MathUtil.LossyPointsOnConvert)
				dest = geoDest;

			actor.getListeners().onMove(actor, dest);

			MoveModel model = new MoveModel();
			model.setWithoutGeodata(false);
			model.setStartMovePoint(current);
			model.setEndMovePoint(dest);
			model.setDistance(current.getDistance(dest));
			moveModel.set(model);
			if(broadcast) {
				validateHeading(current, dest);
				actor.broadcastPacket(new MoveToLocation(actor, dest));
			}


			initTime = System.currentTimeMillis();
			startWatcher(model);
		} finally {
			lock.unlock();
		}
	}

	public void move(int x, int y, int z, int offset, boolean broadcast, int doorid)
	{
		move(x, y, z, offset, broadcast, doorid, true);
	}

	public void move(int x, int y, int z, int offset, boolean broadcast, int doorid, boolean first)
	{
		lock.lock();
		try
		{
			stopMove();

			Location dest;
			if(offset != 0)
				dest = normalizeByOffset(new Location(x, y, z), offset);
			else
				dest = new Location(x, y, z);

			Location current = actor.getLoc();
			double distance2 = current.getDistance2D(dest);

			if(distance2 > MAX_DISTANCE)
			{
				originalEndPoint = dest;
				dest = normalizeByDistance(dest, MAX_DISTANCE);
			}

			dest = checkWay(current, dest);

			actor.getListeners().onMove(actor, dest);

			MoveModel model = new MoveModel();
			model.setWithoutGeodata(false);
			model.setStartMovePoint(current);
			model.setEndMovePoint(dest);
			model.setDistance(current.getDistance(dest));
			moveModel.set(model);

			validateHeading(current, dest);
			if(broadcast)
				actor.broadcastPacket(new MoveToLocation(actor, dest));

			if (first)
				initTime = System.currentTimeMillis();

			startWatcher(model);
		}
		finally
		{
			lock.unlock();
		}
	}

	private Location normalizeByDistance(Location dest, double maxDistance)
	{
		Location current = actor.getLoc();
		double distance = current.getDistance2D(dest);
		if(distance <= maxDistance)
			return dest;

		double divider = maxDistance / distance;

		int x = current.getX() + (int)Math.round(divider * (dest.getX() - current.getX()));
		int y = current.getY() + (int)Math.round(divider * (dest.getY() - current.getY()));
		if(GeoData.getInstance().isEnabled() && !GeoData.getInstance().hasGeo(x, y))
			return null;

		int z;
		if(GeoData.getInstance().isEnabled())
			z = GeoData.getInstance().getMoveHeight(x, y, dest.getZ());
		else
			z = dest.getZ();
		return new Location(x, y, z);
	}

	private Location normalizeByOffset(Location dest, int offset)
	{
		Location current = actor.getLoc();

		double distance = current.getDistance(dest);

		double cos = (dest.getX() - current.getX()) / distance;
		double sin = (dest.getY() - current.getY()) / distance;

		int x = current.getX() + (int)((distance - offset + 10) * cos);
		int y = current.getY() + (int)((distance - offset + 10) * sin);

		if(GeoData.getInstance().isEnabled() && !GeoData.getInstance().hasGeo(x, y))
			return null;

		int z;
		if(GeoData.getInstance().isEnabled())
			z = GeoData.getInstance().getMoveHeight(x, y, dest.getZ());
		else
			z = dest.getZ();
		return new Location(x, y, z);
	}

	private Location cutByGeodata(Location dest, int doorid)
	{
		if(!GeoData.getInstance().isEnabled())
			return dest;

		if (actor.getLoc() == null)
			log.error("cutByGeodata({}): {} have loc is null.", dest, actor);
		if (dest == null)
			log.error("cutByGeodata(NULL) have dest is null for actor: {}", actor);


		return GeoData.getInstance().moveCheck(actor.getLoc(), dest, actor.getInstanceId(), doorid);
	}

	/**
	 * Метод проверяет маршрут персонажа, при необходимости - обрезает или создает путь.
	 * @param current
	 * @param dest
	 * @return
	 */
	private Location checkWay(Location current, Location dest)
	{
		Location geoLockBlocked = cutByGeodata(dest, -1);

		dropItem(geoLockBlocked.getX(), geoLockBlocked.getY(), geoLockBlocked.getZ(), 65);

		//debug(String.format("checkWay: [%s] | %s -> %s", current, dest, geoLockBlocked));

		// если расстояние от блокирущего блока до целевой точки больше размера гео-блока необходимо искать путь.
		if(dest.getDistance2D(geoLockBlocked) != 0)
		{
			path = PathFinding.getInstance().findPath(current.getX(), current.getY(), current.getZ(), dest.getX(), dest.getY(), dest.getZ(), actor.getInstanceId(), actor.isPlayer());
			if(path != null && !path.isEmpty())
			{
				dest = Location.fromNodeLoc(path.remove(0));
				//debug(String.format("checkWay(%d,%d,%d) path size: %d | next: (%s) path: %s", dest.getX(), dest.getY(), dest.getZ(), path.size(), dest, path));
				dropItem(dest.getX(), dest.getY(), dest.getZ(), 1062);
			}
			else
			{
				dest = normalizeByOffset(geoLockBlocked, MathUtil.LossyPointsOnConvert * 2);
				//debug(String.format("move(%d,%d,%d) path is empty", dest.getX(), dest.getY(), dest.getZ()));
			}
		}

		if (actor.hasDebuger())
		{
			double a = current.getDistance2D(dest);
			double c = current.getDistance(dest);

			//debug(String.format("Path[%s]|[%s]", current, dest));
			//debug(String.format("-> a/c: sin: %.2f, cos: %.2f, asin: %.2f, acos: %.2f",  Math.toDegrees(Math.sin(a/c)), Math.toDegrees(Math.cos(a/c)), Math.toDegrees(Math.asin(a/c)), Math.toDegrees(Math.acos(a/c))));
			//debug(String.format("-> c/a: sin: %.2f, cos: %.2f, asin: %.2f, acos: %.2f",  Math.toDegrees(Math.sin(c/a)), Math.toDegrees(Math.cos(c/a)), Math.toDegrees(Math.asin(c/a)), Math.toDegrees(Math.acos(c/a))));
		}

		return dest;
	}

	private boolean validateHeading(Location current, Location dest) {
		int heading = Util.calculateNormalHeading(current.getX(), current.getY(), dest.getX(), dest.getY());
		if(MathUtil.isInRange(actor.getHeading() - 10, actor.getHeading() + 10, heading))
			return false;
		actor.setHeading(heading);
		return true;
	}

	private void completeMove(MoveModel oldModel)
	{
		if(!moveModel.compareAndSet(oldModel, null))
			return;

		if(path == null || path.isEmpty())
		{
			Location originalEndPoint = this.originalEndPoint;
			L2Character pawnTarget = this.pawnTarget;

			stopMove();

			if(originalEndPoint != null) //путь был обрезан из-за длины. продолжаем движение
			{
				move(originalEndPoint.getX(), originalEndPoint.getY(), originalEndPoint.getZ(), 0, true, -1, false);
				return;
			}

			if(pawnTarget != null && pawnTarget.getDistance(actor.getLoc()) > pawnOffset)
			{
				ParallelManager.getInstance().notify(actor.getAI(), CtrlEvent.EVT_ARRIVED_BLOCKED, actor.getLoc());
				return;
			}

			ParallelManager.getInstance().notify(actor.getAI(), CtrlEvent.EVT_ARRIVED);
			return;
		}

		lock.lock();
		try
		{
			Location sp = actor.getLoc();
			Location ep = Location.fromNodeLoc(path.remove(0));
			double dist = sp.getDistance(ep);

			if (actor.hasDebuger())
			{
				double a = sp.getDistance2D(ep);
				double c = sp.getDistance(ep);

				//debug(String.format("Path[%s]|[%s]", sp, ep));
				//debug(String.format("-> a/c: sin: %.2f, cos: %.2f, asin: %.2f, acos: %.2f",  Math.toDegrees(Math.sin(a/c)), Math.toDegrees(Math.cos(a/c)), Math.toDegrees(Math.asin(a/c)), Math.toDegrees(Math.acos(a/c))));
				//debug(String.format("-> c/a: sin: %.2f, cos: %.2f, asin: %.2f, acos: %.2f",  Math.toDegrees(Math.sin(c/a)), Math.toDegrees(Math.cos(c/a)), Math.toDegrees(Math.asin(c/a)), Math.toDegrees(Math.acos(c/a))));
			}
			/*if()
			{
				ParallelManager.getInstance().notify(actor.getAI(), CtrlEvent.EVT_ARRIVED_BLOCKED, actor.getLoc());
				return;
			}*/

			MoveModel model = new MoveModel();
			model.setStartMovePoint(sp);
			model.setEndMovePoint(ep);
			model.setDistance(dist);
			model.setWithoutGeodata(false);
			if(!moveModel.compareAndSet(null, model))
				return;

			validateHeading(sp, ep);
			actor.broadcastPacket(new MoveToLocation(actor, ep));
			startWatcher(model);
		}
		finally
		{
			lock.unlock();
		}
	}


	private void dropItem(int x, int y, int z, int itemId)
	{
		dropItem(x, y, z, itemId, 1);
	}

	private void dropItem(int x, int y, int z, int itemId, int count)
	{
		if(actor.hasDebuger())
		{
			final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
			item.setCount(count);
			item.dropMe(null, x, y, z);
			ThreadPoolManager.getInstance().schedule(new Runnable()
			{
				@Override
				public void run()
				{
					item.decayMe();
				}
			}, 300000);
		}
	}

	private long getTime(L2Character actor)
	{
		return Math.round((MathUtil.LossyPointsOnConvert * 1000d) / actor.getStat().getMoveSpeed());
	}

	private void debug(String text)
	{
		if (actor.hasDebuger() && actor.isPlayer())
			actor.debug(text);
	}

	private class RunnableWatch implements Runnable
	{
		@Override
		public void run()
		{
			watch();
		}
	}

	private class RunnableWatchPawn implements Runnable
	{
		private final L2Character pawn;

		private RunnableWatchPawn(L2Character pawn)
		{
			this.pawn = pawn;
		}

		@Override
		public void run()
		{
			watchPawn(pawn);
		}
	}
}

Edited by Lancer

0 answers to this question

Recommended Posts

There have been no answers to this question yet

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...