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.



  • Posts

    • My official facebook profile!: https://www.facebook.com/spectrumL2 Specifications: Revamped L2JACIS revision FROM the core Private project!!! Revision that has been receiving corrections for over 3 years!!! Events already installed in the revision: TVT CTF KTB PARTY FARM SPOIL EVENT CRAZY RATES TOURNAMENT TIME ZONE (INSTANCE) All working correctly!!! SIEGE ESSENTIAL FEATURES: Walls fix Gates fix Flags fix 100% functional: OLYMPIADS: Implemented settings Hero receives enchanted Weapons with equal status PvP Weapons Optional /true/false Hero can acquire all Hero Weapons Optional true/false OTHER IMPLEMENTATIONS: Teleport fixed (directly to Giran) Teleport effect classic Vip skins vip collor name Pack NPCs with effect already configured BOSES already configured Mobs already configured CLASS BALANCE SPECIAL SYSTEM We have a SPECIAL system developed for Class Balance with only 1 digit in XML %tage of configurable debuffs Player limitation system in BOSES or PvP zones BS blocking system in FLEG zones or events Among others dozens of improvements made in the review... price: 390 USD !  OBS: WE CAN CHANGE THE BANNER AND NAME OF THE SERVICE TO THE ONE OF YOUR PREFERENCE BUT THE SETTINGS MUST BE KEPT ANY CHANGES REQUIRE ADDITION        
    • Server is Online – 1,000+ Active Players! We’re excited to announce the addition of a Europe Proxy to improve connectivity for our EU players! Clans can now benefit from VIP Access to help you catch up faster. 🎯 If you're a clan leader with at least 9 active members, join our Discord and open a ticket to claim your VIP rewards!  
    • The Telegram team is rolling out a new batch of Stars-only gifts you’ll be able to mint as NFTs. Don’t miss your chance to join the next Telegram trend and earn from it! Buy Telegram Stars cheap and KYC-free 1 Star from $0.0149 (min. 50 Stars, bulk discounts available) Promo code STARS5 — 5 % off Pay any way you like: bank cards · crypto · other popular methods How to purchase: ➡Online Store — Click ➡ Telegram bot — Click Other services: ➡ SMM panel — Click Regular buyers get extra discounts and promo codes. Support: ➡ Telegram: https://t.me/solomon_bog ➡ Telegram channel: https://t.me/accsforyou_shop ➡ Discord: https://discord.gg/y9AStFFsrh ➡ WhatsApp: https://wa.me/79051904467 ➡ Email: solomonbog@socnet.store Use these contacts to discuss wholesale orders, partnerships (current list: https://socnet.bgng.io/partners) or to become a supplier. SocNet — your shop for digital goods and premium subscriptions
  • Topics

×
×
  • Create New...

AdBlock Extension Detected!

Our website is made possible by displaying online advertisements to our members.

Please disable AdBlock browser extension first, to be able to use our community.

I've Disabled AdBlock