AI movement rework for return to spawn and long range paths.
This commit is contained in:
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1745,6 +1745,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1691,6 +1691,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1691,6 +1691,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1691,6 +1691,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1679,6 +1679,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1679,6 +1679,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1679,6 +1679,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1690,6 +1690,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3468,10 +3468,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3511,31 +3511,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3550,6 +3574,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1690,6 +1690,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3468,10 +3468,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3511,31 +3511,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3550,6 +3574,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+18
-6
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
+1
@@ -1690,6 +1690,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3468,10 +3468,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3511,31 +3511,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3550,6 +3574,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1690,6 +1690,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3467,10 +3467,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3510,31 +3510,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3549,6 +3573,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
@@ -1690,6 +1690,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3467,10 +3467,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3510,31 +3510,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3549,6 +3573,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -164,12 +164,6 @@ AllowLowLevelTrade = True
|
|||||||
# Retail: 56 Level
|
# Retail: 56 Level
|
||||||
CrumaTowerLevelRestrict = 56
|
CrumaTowerLevelRestrict = 56
|
||||||
|
|
||||||
# Delay before teleporting monsters back to spawn if past drift range (in seconds).
|
|
||||||
# Every 20 minutes (1200) monsters if blocked by Geodata are teleported to their spawn.
|
|
||||||
# Use 0 to disable.
|
|
||||||
# Default: 1200
|
|
||||||
MonsterReturnDelay = 1200
|
|
||||||
|
|
||||||
# If you have made enchant scrolls stackable set this to true.
|
# If you have made enchant scrolls stackable set this to true.
|
||||||
# Default: False
|
# Default: False
|
||||||
ScrollStackable = False
|
ScrollStackable = False
|
||||||
|
|||||||
@@ -632,7 +632,6 @@ public class Config
|
|||||||
public static boolean ALLOW_RAID_BOSS_PETRIFIED;
|
public static boolean ALLOW_RAID_BOSS_PETRIFIED;
|
||||||
public static boolean ALLOW_LOW_LEVEL_TRADE;
|
public static boolean ALLOW_LOW_LEVEL_TRADE;
|
||||||
public static boolean USE_CHAT_FILTER;
|
public static boolean USE_CHAT_FILTER;
|
||||||
public static int MONSTER_RETURN_DELAY;
|
|
||||||
public static boolean SCROLL_STACKABLE;
|
public static boolean SCROLL_STACKABLE;
|
||||||
public static boolean ALLOW_CHAR_KILL_PROTECT;
|
public static boolean ALLOW_CHAR_KILL_PROTECT;
|
||||||
public static int CLAN_LEADER_COLOR;
|
public static int CLAN_LEADER_COLOR;
|
||||||
@@ -1876,7 +1875,6 @@ public class Config
|
|||||||
HERO_COUNT = customServerConfig.getInt("HeroCount", 1);
|
HERO_COUNT = customServerConfig.getInt("HeroCount", 1);
|
||||||
CRUMA_TOWER_LEVEL_RESTRICT = customServerConfig.getInt("CrumaTowerLevelRestrict", 56);
|
CRUMA_TOWER_LEVEL_RESTRICT = customServerConfig.getInt("CrumaTowerLevelRestrict", 56);
|
||||||
ALLOW_RAID_BOSS_PETRIFIED = customServerConfig.getBoolean("AllowRaidBossPetrified", true);
|
ALLOW_RAID_BOSS_PETRIFIED = customServerConfig.getBoolean("AllowRaidBossPetrified", true);
|
||||||
MONSTER_RETURN_DELAY = customServerConfig.getInt("MonsterReturnDelay", 1200);
|
|
||||||
SCROLL_STACKABLE = customServerConfig.getBoolean("ScrollStackable", false);
|
SCROLL_STACKABLE = customServerConfig.getBoolean("ScrollStackable", false);
|
||||||
ALLOW_CHAR_KILL_PROTECT = customServerConfig.getBoolean("AllowLowLvlProtect", false);
|
ALLOW_CHAR_KILL_PROTECT = customServerConfig.getBoolean("AllowLowLvlProtect", false);
|
||||||
CLAN_LEADER_COLOR_ENABLED = customServerConfig.getBoolean("ClanLeaderNameColorEnabled", true);
|
CLAN_LEADER_COLOR_ENABLED = customServerConfig.getBoolean("ClanLeaderNameColorEnabled", true);
|
||||||
|
|||||||
@@ -478,21 +478,29 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a Guard
|
|
||||||
if (_actor instanceof Guard)
|
|
||||||
{
|
|
||||||
// Order to the Guard to return to its home location because there's no target to attack
|
|
||||||
((Guard) _actor).returnHome();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a festival monster, then it remains in the same location.
|
// If this is a festival monster, then it remains in the same location.
|
||||||
if (_actor instanceof FestivalMonster)
|
// if (npc instanceof FestivalMonster)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Check if the mob should not return to spawn point
|
||||||
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.isWalker() && ((getTarget() == null) || (getTarget().isPlayer() && (!getTarget().getActingPlayer().isAlikeDead() || getTarget().getActingPlayer().getAppearance().isInvisible()))))
|
||||||
|
{
|
||||||
|
npc.setWalking();
|
||||||
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -570,11 +578,6 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((Config.MONSTER_RETURN_DELAY > 0) && (npc instanceof Monster) && !npc.isAlikeDead() && !npc.isDead() && (npc.getSpawn() != null) && !npc.isInsideRadius2D(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ(), Config.MAX_DRIFT_RANGE))
|
|
||||||
{
|
|
||||||
((Monster) _actor).returnHome();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If NPC with fixed coord
|
// If NPC with fixed coord
|
||||||
x1 = (npc.getSpawn().getX() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
x1 = (npc.getSpawn().getX() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
||||||
y1 = (npc.getSpawn().getY() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
y1 = (npc.getSpawn().getY() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.l2jmobius.gameserver.enums.ItemLocation;
|
|||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.Skill;
|
import org.l2jmobius.gameserver.model.Skill;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
@@ -741,6 +742,10 @@ public class CreatureAI extends AbstractAI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_actor.isNpc() && _actor.isAttackable())
|
||||||
|
{
|
||||||
|
((Attackable) _actor).setReturningToSpawnPoint(false);
|
||||||
|
}
|
||||||
clientStoppedMoving();
|
clientStoppedMoving();
|
||||||
|
|
||||||
// If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE
|
// If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE
|
||||||
|
|||||||
+21
@@ -18,6 +18,7 @@ package org.l2jmobius.gameserver.handler.admincommandhandlers;
|
|||||||
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.l2jmobius.gameserver.ai.CtrlIntention;
|
||||||
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
|
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -108,6 +109,16 @@ public class AdminEffects implements IAdminCommandHandler
|
|||||||
activeChar.decayMe();
|
activeChar.decayMe();
|
||||||
activeChar.broadcastUserInfo();
|
activeChar.broadcastUserInfo();
|
||||||
activeChar.spawnMe();
|
activeChar.spawnMe();
|
||||||
|
for (Creature target : activeChar.getKnownList().getKnownCharacters())
|
||||||
|
{
|
||||||
|
if ((target != null) && (target.getTarget() == activeChar))
|
||||||
|
{
|
||||||
|
target.setTarget(null);
|
||||||
|
target.abortAttack();
|
||||||
|
target.abortCast();
|
||||||
|
target.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -123,6 +134,16 @@ public class AdminEffects implements IAdminCommandHandler
|
|||||||
activeChar.decayMe();
|
activeChar.decayMe();
|
||||||
activeChar.broadcastUserInfo();
|
activeChar.broadcastUserInfo();
|
||||||
activeChar.spawnMe();
|
activeChar.spawnMe();
|
||||||
|
for (Creature target : activeChar.getKnownList().getKnownCharacters())
|
||||||
|
{
|
||||||
|
if ((target != null) && (target.getTarget() == activeChar))
|
||||||
|
{
|
||||||
|
target.setTarget(null);
|
||||||
|
target.abortAttack();
|
||||||
|
target.abortCast();
|
||||||
|
target.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
||||||
}
|
}
|
||||||
else if (command.startsWith("admin_vis"))
|
else if (command.startsWith("admin_vis"))
|
||||||
|
|||||||
@@ -580,6 +580,11 @@ public abstract class WorldObject
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isWalker()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates 2D distance between this WorldObject and given x, y, z.
|
* Calculates 2D distance between this WorldObject and given x, y, z.
|
||||||
* @param x the X coordinate
|
* @param x the X coordinate
|
||||||
|
|||||||
+12
@@ -38,6 +38,7 @@ import org.l2jmobius.gameserver.instancemanager.EventDropManager;
|
|||||||
import org.l2jmobius.gameserver.model.CommandChannel;
|
import org.l2jmobius.gameserver.model.CommandChannel;
|
||||||
import org.l2jmobius.gameserver.model.DropCategory;
|
import org.l2jmobius.gameserver.model.DropCategory;
|
||||||
import org.l2jmobius.gameserver.model.DropData;
|
import org.l2jmobius.gameserver.model.DropData;
|
||||||
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.Party;
|
import org.l2jmobius.gameserver.model.Party;
|
||||||
import org.l2jmobius.gameserver.model.SoulCrystal;
|
import org.l2jmobius.gameserver.model.SoulCrystal;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Door;
|
import org.l2jmobius.gameserver.model.actor.instance.Door;
|
||||||
@@ -2688,6 +2689,17 @@ public class Attackable extends Npc
|
|||||||
_commandChannelLastAttack = channelLastAttack;
|
_commandChannelLastAttack = channelLastAttack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void returnHome()
|
||||||
|
{
|
||||||
|
clearAggroList();
|
||||||
|
|
||||||
|
if (hasAI() && (getSpawn() != null))
|
||||||
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class CommandChannelTimer implements Runnable
|
private static class CommandChannelTimer implements Runnable
|
||||||
{
|
{
|
||||||
private final Attackable _monster;
|
private final Attackable _monster;
|
||||||
|
|||||||
+61
-23
@@ -5343,10 +5343,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if (Config.PATHFINDING > 0)
|
if (Config.PATHFINDING > 0)
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -5406,31 +5406,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isNpc() && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceId(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -5445,6 +5469,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -110,9 +110,7 @@ public class Commander extends Attackable
|
|||||||
return _homeY;
|
return _homeY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This method forces guard to return to home location previously set
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
||||||
|
|||||||
+2
-3
@@ -100,15 +100,14 @@ public class FortSiegeGuard extends Attackable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This method forces guard to return to home location previously set
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (getWalkSpeed() <= 0)
|
if (getWalkSpeed() <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInsideRadius2D(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ(), 40))
|
if (!isInsideRadius2D(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ(), 40))
|
||||||
{
|
{
|
||||||
setReturningToSpawnPoint(true);
|
setReturningToSpawnPoint(true);
|
||||||
|
|||||||
+2
-3
@@ -118,14 +118,13 @@ public class Guard extends Attackable
|
|||||||
return _homeX;
|
return _homeX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Notify the GuardInstance to return to its home location (AI_INTENTION_MOVE_TO) and clear its _aggroList.
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 150))
|
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 150))
|
||||||
{
|
{
|
||||||
clearAggroList();
|
clearAggroList();
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(_homeX, _homeY, _homeZ, 0));
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(_homeX, _homeY, _homeZ, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-13
@@ -26,7 +26,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.knownlist.MonsterKnownList;
|
import org.l2jmobius.gameserver.model.actor.knownlist.MonsterKnownList;
|
||||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||||
import org.l2jmobius.gameserver.model.spawn.Spawn;
|
|
||||||
import org.l2jmobius.gameserver.util.MinionList;
|
import org.l2jmobius.gameserver.util.MinionList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,18 +67,6 @@ public class Monster extends Attackable
|
|||||||
return (MonsterKnownList) super.getKnownList();
|
return (MonsterKnownList) super.getKnownList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void returnHome()
|
|
||||||
{
|
|
||||||
ThreadPool.schedule(() ->
|
|
||||||
{
|
|
||||||
final Spawn mobSpawn = getSpawn();
|
|
||||||
if (!isInCombat() && !isAlikeDead() && !isDead() && (mobSpawn != null) && !isInsideRadius2D(mobSpawn.getX(), mobSpawn.getY(), mobSpawn.getZ(), Config.MAX_DRIFT_RANGE))
|
|
||||||
{
|
|
||||||
teleToLocation(mobSpawn.getX(), mobSpawn.getY(), mobSpawn.getZ());
|
|
||||||
}
|
|
||||||
}, Config.MONSTER_RETURN_DELAY * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return True if the attacker is not another Monster.
|
* Return True if the attacker is not another Monster.
|
||||||
* @param attacker the attacker
|
* @param attacker the attacker
|
||||||
|
|||||||
+6
@@ -68,6 +68,12 @@ public class NpcWalker extends Npc
|
|||||||
((NpcWalkerAI) getAI()).setHomeZ(getZ());
|
((NpcWalkerAI) getAI()).setHomeZ(getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWalker()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a chat to all _knowObjects.
|
* Sends a chat to all _knowObjects.
|
||||||
* @param chat message to say
|
* @param chat message to say
|
||||||
|
|||||||
+1
-3
@@ -109,9 +109,7 @@ public class SiegeGuard extends Attackable
|
|||||||
return _homeY;
|
return _homeY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This method forces guard to return to home location previously set
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import java.util.logging.Logger;
|
|||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.sql.TerritoryTable;
|
import org.l2jmobius.gameserver.data.sql.TerritoryTable;
|
||||||
import org.l2jmobius.gameserver.data.xml.WalkerRouteData;
|
|
||||||
import org.l2jmobius.gameserver.data.xml.ZoneData;
|
import org.l2jmobius.gameserver.data.xml.ZoneData;
|
||||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||||
import org.l2jmobius.gameserver.instancemanager.IdManager;
|
import org.l2jmobius.gameserver.instancemanager.IdManager;
|
||||||
@@ -449,7 +448,7 @@ public class Spawn
|
|||||||
final WaterZone water = ZoneData.getInstance().getZone(newlocx, newlocy, newlocz, WaterZone.class);
|
final WaterZone water = ZoneData.getInstance().getZone(newlocx, newlocy, newlocz, WaterZone.class);
|
||||||
|
|
||||||
// If random spawn system is enabled.
|
// If random spawn system is enabled.
|
||||||
if (Config.ENABLE_RANDOM_MONSTER_SPAWNS && npc.isMonster() && !npc.isQuestMonster() && (WalkerRouteData.getInstance().getRouteForNpc(npc.getNpcId()) == null) && (getInstanceId() == 0) && !npc.isRaid() && !npc.isMinion() && !npc.isFlying() && (water == null) && !Config.MOBS_LIST_NOT_RANDOM.contains(npc.getNpcId()))
|
if (Config.ENABLE_RANDOM_MONSTER_SPAWNS && npc.isMonster() && !npc.isQuestMonster() && !npc.isWalker() && (getInstanceId() == 0) && !npc.isRaid() && !npc.isMinion() && !npc.isFlying() && (water == null) && !Config.MOBS_LIST_NOT_RANDOM.contains(npc.getNpcId()))
|
||||||
{
|
{
|
||||||
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
||||||
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
||||||
|
|||||||
@@ -164,12 +164,6 @@ AllowLowLevelTrade = True
|
|||||||
# Retail: 56 Level
|
# Retail: 56 Level
|
||||||
CrumaTowerLevelRestrict = 56
|
CrumaTowerLevelRestrict = 56
|
||||||
|
|
||||||
# Delay before teleporting monsters back to spawn if past drift range (in seconds).
|
|
||||||
# Every 20 minutes (1200) monsters if blocked by Geodata are teleported to their spawn.
|
|
||||||
# Use 0 to disable.
|
|
||||||
# Default: 1200
|
|
||||||
MonsterReturnDelay = 1200
|
|
||||||
|
|
||||||
# If you have made enchant scrolls stackable set this to true.
|
# If you have made enchant scrolls stackable set this to true.
|
||||||
# Default: False
|
# Default: False
|
||||||
ScrollStackable = False
|
ScrollStackable = False
|
||||||
|
|||||||
@@ -660,7 +660,6 @@ public class Config
|
|||||||
public static boolean ALLOW_RAID_BOSS_PETRIFIED;
|
public static boolean ALLOW_RAID_BOSS_PETRIFIED;
|
||||||
public static boolean ALLOW_LOW_LEVEL_TRADE;
|
public static boolean ALLOW_LOW_LEVEL_TRADE;
|
||||||
public static boolean USE_CHAT_FILTER;
|
public static boolean USE_CHAT_FILTER;
|
||||||
public static int MONSTER_RETURN_DELAY;
|
|
||||||
public static boolean SCROLL_STACKABLE;
|
public static boolean SCROLL_STACKABLE;
|
||||||
public static boolean ALLOW_CHAR_KILL_PROTECT;
|
public static boolean ALLOW_CHAR_KILL_PROTECT;
|
||||||
public static int CLAN_LEADER_COLOR;
|
public static int CLAN_LEADER_COLOR;
|
||||||
@@ -1929,7 +1928,6 @@ public class Config
|
|||||||
HERO_COUNT = customServerConfig.getInt("HeroCount", 1);
|
HERO_COUNT = customServerConfig.getInt("HeroCount", 1);
|
||||||
CRUMA_TOWER_LEVEL_RESTRICT = customServerConfig.getInt("CrumaTowerLevelRestrict", 56);
|
CRUMA_TOWER_LEVEL_RESTRICT = customServerConfig.getInt("CrumaTowerLevelRestrict", 56);
|
||||||
ALLOW_RAID_BOSS_PETRIFIED = customServerConfig.getBoolean("AllowRaidBossPetrified", true);
|
ALLOW_RAID_BOSS_PETRIFIED = customServerConfig.getBoolean("AllowRaidBossPetrified", true);
|
||||||
MONSTER_RETURN_DELAY = customServerConfig.getInt("MonsterReturnDelay", 1200);
|
|
||||||
SCROLL_STACKABLE = customServerConfig.getBoolean("ScrollStackable", false);
|
SCROLL_STACKABLE = customServerConfig.getBoolean("ScrollStackable", false);
|
||||||
ALLOW_CHAR_KILL_PROTECT = customServerConfig.getBoolean("AllowLowLvlProtect", false);
|
ALLOW_CHAR_KILL_PROTECT = customServerConfig.getBoolean("AllowLowLvlProtect", false);
|
||||||
CLAN_LEADER_COLOR_ENABLED = customServerConfig.getBoolean("ClanLeaderNameColorEnabled", true);
|
CLAN_LEADER_COLOR_ENABLED = customServerConfig.getBoolean("ClanLeaderNameColorEnabled", true);
|
||||||
|
|||||||
@@ -478,21 +478,29 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a Guard
|
|
||||||
if (_actor instanceof Guard)
|
|
||||||
{
|
|
||||||
// Order to the Guard to return to its home location because there's no target to attack
|
|
||||||
((Guard) _actor).returnHome();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a festival monster, then it remains in the same location.
|
// If this is a festival monster, then it remains in the same location.
|
||||||
if (_actor instanceof FestivalMonster)
|
// if (npc instanceof FestivalMonster)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Check if the mob should not return to spawn point
|
||||||
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.isWalker() && ((getTarget() == null) || (getTarget().isPlayer() && (!getTarget().getActingPlayer().isAlikeDead() || getTarget().getActingPlayer().getAppearance().isInvisible()))))
|
||||||
|
{
|
||||||
|
npc.setWalking();
|
||||||
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -570,11 +578,6 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((Config.MONSTER_RETURN_DELAY > 0) && (npc instanceof Monster) && !npc.isAlikeDead() && !npc.isDead() && (npc.getSpawn() != null) && !npc.isInsideRadius2D(npc.getSpawn().getX(), npc.getSpawn().getY(), npc.getSpawn().getZ(), Config.MAX_DRIFT_RANGE))
|
|
||||||
{
|
|
||||||
((Monster) _actor).returnHome();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If NPC with fixed coord
|
// If NPC with fixed coord
|
||||||
x1 = (npc.getSpawn().getX() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
x1 = (npc.getSpawn().getX() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
||||||
y1 = (npc.getSpawn().getY() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
y1 = (npc.getSpawn().getY() + Rnd.get(Config.MAX_DRIFT_RANGE * 2)) - Config.MAX_DRIFT_RANGE;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.l2jmobius.gameserver.enums.ItemLocation;
|
|||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.Skill;
|
import org.l2jmobius.gameserver.model.Skill;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
@@ -741,6 +742,10 @@ public class CreatureAI extends AbstractAI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_actor.isNpc() && _actor.isAttackable())
|
||||||
|
{
|
||||||
|
((Attackable) _actor).setReturningToSpawnPoint(false);
|
||||||
|
}
|
||||||
clientStoppedMoving();
|
clientStoppedMoving();
|
||||||
|
|
||||||
// If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE
|
// If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE
|
||||||
|
|||||||
+21
@@ -18,6 +18,7 @@ package org.l2jmobius.gameserver.handler.admincommandhandlers;
|
|||||||
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.l2jmobius.gameserver.ai.CtrlIntention;
|
||||||
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
|
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -108,6 +109,16 @@ public class AdminEffects implements IAdminCommandHandler
|
|||||||
activeChar.decayMe();
|
activeChar.decayMe();
|
||||||
activeChar.broadcastUserInfo();
|
activeChar.broadcastUserInfo();
|
||||||
activeChar.spawnMe();
|
activeChar.spawnMe();
|
||||||
|
for (Creature target : activeChar.getKnownList().getKnownCharacters())
|
||||||
|
{
|
||||||
|
if ((target != null) && (target.getTarget() == activeChar))
|
||||||
|
{
|
||||||
|
target.setTarget(null);
|
||||||
|
target.abortAttack();
|
||||||
|
target.abortCast();
|
||||||
|
target.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -123,6 +134,16 @@ public class AdminEffects implements IAdminCommandHandler
|
|||||||
activeChar.decayMe();
|
activeChar.decayMe();
|
||||||
activeChar.broadcastUserInfo();
|
activeChar.broadcastUserInfo();
|
||||||
activeChar.spawnMe();
|
activeChar.spawnMe();
|
||||||
|
for (Creature target : activeChar.getKnownList().getKnownCharacters())
|
||||||
|
{
|
||||||
|
if ((target != null) && (target.getTarget() == activeChar))
|
||||||
|
{
|
||||||
|
target.setTarget(null);
|
||||||
|
target.abortAttack();
|
||||||
|
target.abortCast();
|
||||||
|
target.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
BuilderUtil.sendSysMessage(activeChar, "Now, you cannot be seen.");
|
||||||
}
|
}
|
||||||
else if (command.startsWith("admin_vis"))
|
else if (command.startsWith("admin_vis"))
|
||||||
|
|||||||
@@ -580,6 +580,11 @@ public abstract class WorldObject
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isWalker()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates 2D distance between this WorldObject and given x, y, z.
|
* Calculates 2D distance between this WorldObject and given x, y, z.
|
||||||
* @param x the X coordinate
|
* @param x the X coordinate
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import org.l2jmobius.gameserver.instancemanager.EventDropManager;
|
|||||||
import org.l2jmobius.gameserver.model.CommandChannel;
|
import org.l2jmobius.gameserver.model.CommandChannel;
|
||||||
import org.l2jmobius.gameserver.model.DropCategory;
|
import org.l2jmobius.gameserver.model.DropCategory;
|
||||||
import org.l2jmobius.gameserver.model.DropData;
|
import org.l2jmobius.gameserver.model.DropData;
|
||||||
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.Party;
|
import org.l2jmobius.gameserver.model.Party;
|
||||||
import org.l2jmobius.gameserver.model.SoulCrystal;
|
import org.l2jmobius.gameserver.model.SoulCrystal;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Door;
|
import org.l2jmobius.gameserver.model.actor.instance.Door;
|
||||||
@@ -3040,6 +3041,17 @@ public class Attackable extends Npc
|
|||||||
_commandChannelLastAttack = channelLastAttack;
|
_commandChannelLastAttack = channelLastAttack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void returnHome()
|
||||||
|
{
|
||||||
|
clearAggroList();
|
||||||
|
|
||||||
|
if (hasAI() && (getSpawn() != null))
|
||||||
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class CommandChannelTimer implements Runnable
|
private static class CommandChannelTimer implements Runnable
|
||||||
{
|
{
|
||||||
private final Attackable _monster;
|
private final Attackable _monster;
|
||||||
|
|||||||
@@ -5389,10 +5389,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if (Config.PATHFINDING > 0)
|
if (Config.PATHFINDING > 0)
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -5452,31 +5452,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isNpc() && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceId(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -5491,6 +5515,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -110,9 +110,7 @@ public class Commander extends Attackable
|
|||||||
return _homeY;
|
return _homeY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This method forces guard to return to home location previously set
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
||||||
|
|||||||
+2
-3
@@ -100,15 +100,14 @@ public class FortSiegeGuard extends Attackable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This method forces guard to return to home location previously set
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (getWalkSpeed() <= 0)
|
if (getWalkSpeed() <= 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isInsideRadius2D(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ(), 40))
|
if (!isInsideRadius2D(getSpawn().getX(), getSpawn().getY(), getSpawn().getZ(), 40))
|
||||||
{
|
{
|
||||||
setReturningToSpawnPoint(true);
|
setReturningToSpawnPoint(true);
|
||||||
|
|||||||
+2
-3
@@ -118,14 +118,13 @@ public class Guard extends Attackable
|
|||||||
return _homeX;
|
return _homeX;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Notify the GuardInstance to return to its home location (AI_INTENTION_MOVE_TO) and clear its _aggroList.
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 150))
|
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 150))
|
||||||
{
|
{
|
||||||
clearAggroList();
|
clearAggroList();
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(_homeX, _homeY, _homeZ, 0));
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(_homeX, _homeY, _homeZ, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-13
@@ -26,7 +26,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.knownlist.MonsterKnownList;
|
import org.l2jmobius.gameserver.model.actor.knownlist.MonsterKnownList;
|
||||||
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate;
|
||||||
import org.l2jmobius.gameserver.model.spawn.Spawn;
|
|
||||||
import org.l2jmobius.gameserver.util.MinionList;
|
import org.l2jmobius.gameserver.util.MinionList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,18 +67,6 @@ public class Monster extends Attackable
|
|||||||
return (MonsterKnownList) super.getKnownList();
|
return (MonsterKnownList) super.getKnownList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void returnHome()
|
|
||||||
{
|
|
||||||
ThreadPool.schedule(() ->
|
|
||||||
{
|
|
||||||
final Spawn mobSpawn = getSpawn();
|
|
||||||
if (!isInCombat() && !isAlikeDead() && !isDead() && (mobSpawn != null) && !isInsideRadius2D(mobSpawn.getX(), mobSpawn.getY(), mobSpawn.getZ(), Config.MAX_DRIFT_RANGE))
|
|
||||||
{
|
|
||||||
teleToLocation(mobSpawn.getX(), mobSpawn.getY(), mobSpawn.getZ());
|
|
||||||
}
|
|
||||||
}, Config.MONSTER_RETURN_DELAY * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return True if the attacker is not another Monster.
|
* Return True if the attacker is not another Monster.
|
||||||
* @param attacker the attacker
|
* @param attacker the attacker
|
||||||
|
|||||||
+6
@@ -68,6 +68,12 @@ public class NpcWalker extends Npc
|
|||||||
((NpcWalkerAI) getAI()).setHomeZ(getZ());
|
((NpcWalkerAI) getAI()).setHomeZ(getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWalker()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a chat to all _knowObjects.
|
* Sends a chat to all _knowObjects.
|
||||||
* @param chat message to say
|
* @param chat message to say
|
||||||
|
|||||||
+1
-3
@@ -109,9 +109,7 @@ public class SiegeGuard extends Attackable
|
|||||||
return _homeY;
|
return _homeY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* This method forces guard to return to home location previously set
|
|
||||||
*/
|
|
||||||
public void returnHome()
|
public void returnHome()
|
||||||
{
|
{
|
||||||
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
if (!isInsideRadius2D(_homeX, _homeY, _homeZ, 40))
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import java.util.logging.Logger;
|
|||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.sql.TerritoryTable;
|
import org.l2jmobius.gameserver.data.sql.TerritoryTable;
|
||||||
import org.l2jmobius.gameserver.data.xml.WalkerRouteData;
|
|
||||||
import org.l2jmobius.gameserver.data.xml.ZoneData;
|
import org.l2jmobius.gameserver.data.xml.ZoneData;
|
||||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||||
import org.l2jmobius.gameserver.instancemanager.IdManager;
|
import org.l2jmobius.gameserver.instancemanager.IdManager;
|
||||||
@@ -449,7 +448,7 @@ public class Spawn
|
|||||||
final WaterZone water = ZoneData.getInstance().getZone(newlocx, newlocy, newlocz, WaterZone.class);
|
final WaterZone water = ZoneData.getInstance().getZone(newlocx, newlocy, newlocz, WaterZone.class);
|
||||||
|
|
||||||
// If random spawn system is enabled.
|
// If random spawn system is enabled.
|
||||||
if (Config.ENABLE_RANDOM_MONSTER_SPAWNS && npc.isMonster() && !npc.isQuestMonster() && (WalkerRouteData.getInstance().getRouteForNpc(npc.getNpcId()) == null) && (getInstanceId() == 0) && !npc.isRaid() && !npc.isMinion() && !npc.isFlying() && (water == null) && !Config.MOBS_LIST_NOT_RANDOM.contains(npc.getNpcId()))
|
if (Config.ENABLE_RANDOM_MONSTER_SPAWNS && npc.isMonster() && !npc.isQuestMonster() && !npc.isWalker() && (getInstanceId() == 0) && !npc.isRaid() && !npc.isMinion() && !npc.isFlying() && (water == null) && !Config.MOBS_LIST_NOT_RANDOM.contains(npc.getNpcId()))
|
||||||
{
|
{
|
||||||
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
final int randX = newlocx + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
||||||
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
final int randY = newlocy + Rnd.get(Config.MOB_MIN_SPAWN_RANGE, Config.MOB_MAX_SPAWN_RANGE);
|
||||||
|
|||||||
@@ -137,9 +137,6 @@ AggroDistanceCheckRestoreLife = True
|
|||||||
# Default: False
|
# Default: False
|
||||||
GuardAttackAggroMob = False
|
GuardAttackAggroMob = False
|
||||||
|
|
||||||
# True - Allows guards to use return skill after combat.
|
|
||||||
# Default: False
|
|
||||||
EnableGuardReturn = True
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Pets
|
# Pets
|
||||||
|
|||||||
@@ -716,7 +716,6 @@ public class Config
|
|||||||
public static boolean AGGRO_DISTANCE_CHECK_INSTANCES;
|
public static boolean AGGRO_DISTANCE_CHECK_INSTANCES;
|
||||||
public static boolean AGGRO_DISTANCE_CHECK_RESTORE_LIFE;
|
public static boolean AGGRO_DISTANCE_CHECK_RESTORE_LIFE;
|
||||||
public static boolean GUARD_ATTACK_AGGRO_MOB;
|
public static boolean GUARD_ATTACK_AGGRO_MOB;
|
||||||
public static boolean ENABLE_GUARD_RETURN;
|
|
||||||
public static boolean ALLOW_WYVERN_UPGRADER;
|
public static boolean ALLOW_WYVERN_UPGRADER;
|
||||||
public static double RAID_HP_REGEN_MULTIPLIER;
|
public static double RAID_HP_REGEN_MULTIPLIER;
|
||||||
public static double RAID_MP_REGEN_MULTIPLIER;
|
public static double RAID_MP_REGEN_MULTIPLIER;
|
||||||
@@ -2144,7 +2143,6 @@ public class Config
|
|||||||
AGGRO_DISTANCE_CHECK_INSTANCES = npcConfig.getBoolean("AggroDistanceCheckInstances", false);
|
AGGRO_DISTANCE_CHECK_INSTANCES = npcConfig.getBoolean("AggroDistanceCheckInstances", false);
|
||||||
AGGRO_DISTANCE_CHECK_RESTORE_LIFE = npcConfig.getBoolean("AggroDistanceCheckRestoreLife", true);
|
AGGRO_DISTANCE_CHECK_RESTORE_LIFE = npcConfig.getBoolean("AggroDistanceCheckRestoreLife", true);
|
||||||
GUARD_ATTACK_AGGRO_MOB = npcConfig.getBoolean("GuardAttackAggroMob", false);
|
GUARD_ATTACK_AGGRO_MOB = npcConfig.getBoolean("GuardAttackAggroMob", false);
|
||||||
ENABLE_GUARD_RETURN = npcConfig.getBoolean("EnableGuardReturn", false);
|
|
||||||
ALLOW_WYVERN_UPGRADER = npcConfig.getBoolean("AllowWyvernUpgrader", false);
|
ALLOW_WYVERN_UPGRADER = npcConfig.getBoolean("AllowWyvernUpgrader", false);
|
||||||
RAID_HP_REGEN_MULTIPLIER = npcConfig.getDouble("RaidHpRegenMultiplier", 100) / 100;
|
RAID_HP_REGEN_MULTIPLIER = npcConfig.getDouble("RaidHpRegenMultiplier", 100) / 100;
|
||||||
RAID_MP_REGEN_MULTIPLIER = npcConfig.getDouble("RaidMpRegenMultiplier", 100) / 100;
|
RAID_MP_REGEN_MULTIPLIER = npcConfig.getDouble("RaidMpRegenMultiplier", 100) / 100;
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.util.concurrent.Future;
|
|||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.threads.ThreadPool;
|
import org.l2jmobius.commons.threads.ThreadPool;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
import org.l2jmobius.gameserver.enums.AIType;
|
import org.l2jmobius.gameserver.enums.AIType;
|
||||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||||
@@ -602,30 +601,29 @@ public class AttackableAI extends CreatureAI
|
|||||||
npc.getAttackByList().clear();
|
npc.getAttackByList().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a festival monster, then it remains in the same location.
|
||||||
|
// if (npc instanceof FestivalMonster)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a GuardInstance
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if ((npc instanceof Guard) && !npc.isWalker())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
if (Config.ENABLE_GUARD_RETURN && (npc.getSpawn() != null) && (Util.calculateDistance(npc, npc.getSpawn(), false, false) > 50) && /* !npc.isInsideZone(ZoneId.SIEGE) && */!npc.isCastingNow())
|
npc.setWalking();
|
||||||
{
|
npc.returnHome();
|
||||||
// Custom guard teleport to spawn.
|
return;
|
||||||
npc.clearAggroList();
|
|
||||||
npc.doCast(SkillData.getInstance().getSkill(1050, 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a festival monster, then it remains in the same location.
|
// Do not leave dead player
|
||||||
if (npc instanceof FestivalMonster)
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -681,7 +679,6 @@ public class AttackableAI extends CreatureAI
|
|||||||
// Order to the Monster to random walk (1/100)
|
// Order to the Monster to random walk (1/100)
|
||||||
else if ((npc.getSpawn() != null) && (Rnd.get(RANDOM_WALK_RATE) == 0) && npc.isRandomWalkingEnabled())
|
else if ((npc.getSpawn() != null) && (Rnd.get(RANDOM_WALK_RATE) == 0) && npc.isRandomWalkingEnabled())
|
||||||
{
|
{
|
||||||
|
|
||||||
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
|
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
|
||||||
{
|
{
|
||||||
if (cast(sk))
|
if (cast(sk))
|
||||||
|
|||||||
@@ -1687,6 +1687,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4246,10 +4246,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof QuestGuard))
|
if ((Config.PATHFINDING > 0) && !(this instanceof QuestGuard))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -4289,31 +4289,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceId(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -4328,6 +4352,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -41,6 +41,7 @@ public class QuestGuard extends Guard
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.QuestGuard);
|
setInstanceType(InstanceType.QuestGuard);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -137,9 +137,6 @@ AggroDistanceCheckRestoreLife = True
|
|||||||
# Default: False
|
# Default: False
|
||||||
GuardAttackAggroMob = False
|
GuardAttackAggroMob = False
|
||||||
|
|
||||||
# True - Allows guards to use return skill after combat.
|
|
||||||
# Default: False
|
|
||||||
EnableGuardReturn = True
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Pets
|
# Pets
|
||||||
|
|||||||
@@ -741,7 +741,6 @@ public class Config
|
|||||||
public static boolean AGGRO_DISTANCE_CHECK_INSTANCES;
|
public static boolean AGGRO_DISTANCE_CHECK_INSTANCES;
|
||||||
public static boolean AGGRO_DISTANCE_CHECK_RESTORE_LIFE;
|
public static boolean AGGRO_DISTANCE_CHECK_RESTORE_LIFE;
|
||||||
public static boolean GUARD_ATTACK_AGGRO_MOB;
|
public static boolean GUARD_ATTACK_AGGRO_MOB;
|
||||||
public static boolean ENABLE_GUARD_RETURN;
|
|
||||||
public static boolean ALLOW_WYVERN_UPGRADER;
|
public static boolean ALLOW_WYVERN_UPGRADER;
|
||||||
public static List<Integer> LIST_PET_RENT_NPC;
|
public static List<Integer> LIST_PET_RENT_NPC;
|
||||||
public static double RAID_HP_REGEN_MULTIPLIER;
|
public static double RAID_HP_REGEN_MULTIPLIER;
|
||||||
@@ -2234,7 +2233,6 @@ public class Config
|
|||||||
AGGRO_DISTANCE_CHECK_INSTANCES = npcConfig.getBoolean("AggroDistanceCheckInstances", false);
|
AGGRO_DISTANCE_CHECK_INSTANCES = npcConfig.getBoolean("AggroDistanceCheckInstances", false);
|
||||||
AGGRO_DISTANCE_CHECK_RESTORE_LIFE = npcConfig.getBoolean("AggroDistanceCheckRestoreLife", true);
|
AGGRO_DISTANCE_CHECK_RESTORE_LIFE = npcConfig.getBoolean("AggroDistanceCheckRestoreLife", true);
|
||||||
GUARD_ATTACK_AGGRO_MOB = npcConfig.getBoolean("GuardAttackAggroMob", false);
|
GUARD_ATTACK_AGGRO_MOB = npcConfig.getBoolean("GuardAttackAggroMob", false);
|
||||||
ENABLE_GUARD_RETURN = npcConfig.getBoolean("EnableGuardReturn", false);
|
|
||||||
ALLOW_WYVERN_UPGRADER = npcConfig.getBoolean("AllowWyvernUpgrader", false);
|
ALLOW_WYVERN_UPGRADER = npcConfig.getBoolean("AllowWyvernUpgrader", false);
|
||||||
final String[] listPetRentNpc = npcConfig.getString("ListPetRentNpc", "30827").split(",");
|
final String[] listPetRentNpc = npcConfig.getString("ListPetRentNpc", "30827").split(",");
|
||||||
LIST_PET_RENT_NPC = new ArrayList<>(listPetRentNpc.length);
|
LIST_PET_RENT_NPC = new ArrayList<>(listPetRentNpc.length);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.util.concurrent.Future;
|
|||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.threads.ThreadPool;
|
import org.l2jmobius.commons.threads.ThreadPool;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
import org.l2jmobius.gameserver.enums.AIType;
|
import org.l2jmobius.gameserver.enums.AIType;
|
||||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||||
@@ -602,30 +601,29 @@ public class AttackableAI extends CreatureAI
|
|||||||
npc.getAttackByList().clear();
|
npc.getAttackByList().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a festival monster, then it remains in the same location.
|
||||||
|
// if (npc instanceof FestivalMonster)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a GuardInstance
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if ((npc instanceof Guard) && !npc.isWalker())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
if (Config.ENABLE_GUARD_RETURN && (npc.getSpawn() != null) && (Util.calculateDistance(npc, npc.getSpawn(), false, false) > 50) && /* !npc.isInsideZone(ZoneId.SIEGE) && */!npc.isCastingNow())
|
npc.setWalking();
|
||||||
{
|
npc.returnHome();
|
||||||
// Custom guard teleport to spawn.
|
return;
|
||||||
npc.clearAggroList();
|
|
||||||
npc.doCast(SkillData.getInstance().getSkill(1050, 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a festival monster, then it remains in the same location.
|
// Do not leave dead player
|
||||||
if (npc instanceof FestivalMonster)
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -681,7 +679,6 @@ public class AttackableAI extends CreatureAI
|
|||||||
// Order to the Monster to random walk (1/100)
|
// Order to the Monster to random walk (1/100)
|
||||||
else if ((npc.getSpawn() != null) && (Rnd.get(RANDOM_WALK_RATE) == 0) && npc.isRandomWalkingEnabled())
|
else if ((npc.getSpawn() != null) && (Rnd.get(RANDOM_WALK_RATE) == 0) && npc.isRandomWalkingEnabled())
|
||||||
{
|
{
|
||||||
|
|
||||||
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
|
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
|
||||||
{
|
{
|
||||||
if (cast(sk))
|
if (cast(sk))
|
||||||
|
|||||||
@@ -1688,6 +1688,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -4431,10 +4431,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof QuestGuard))
|
if ((Config.PATHFINDING > 0) && !(this instanceof QuestGuard))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -4474,31 +4474,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceId(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -4513,6 +4537,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -41,6 +41,7 @@ public class QuestGuard extends Guard
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.QuestGuard);
|
setInstanceType(InstanceType.QuestGuard);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -137,9 +137,6 @@ AggroDistanceCheckRestoreLife = True
|
|||||||
# Default: False
|
# Default: False
|
||||||
GuardAttackAggroMob = False
|
GuardAttackAggroMob = False
|
||||||
|
|
||||||
# True - Allows guards to use return skill after combat.
|
|
||||||
# Default: False
|
|
||||||
EnableGuardReturn = True
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Pets
|
# Pets
|
||||||
|
|||||||
@@ -746,7 +746,6 @@ public class Config
|
|||||||
public static boolean AGGRO_DISTANCE_CHECK_INSTANCES;
|
public static boolean AGGRO_DISTANCE_CHECK_INSTANCES;
|
||||||
public static boolean AGGRO_DISTANCE_CHECK_RESTORE_LIFE;
|
public static boolean AGGRO_DISTANCE_CHECK_RESTORE_LIFE;
|
||||||
public static boolean GUARD_ATTACK_AGGRO_MOB;
|
public static boolean GUARD_ATTACK_AGGRO_MOB;
|
||||||
public static boolean ENABLE_GUARD_RETURN;
|
|
||||||
public static boolean ALLOW_WYVERN_UPGRADER;
|
public static boolean ALLOW_WYVERN_UPGRADER;
|
||||||
public static List<Integer> LIST_PET_RENT_NPC;
|
public static List<Integer> LIST_PET_RENT_NPC;
|
||||||
public static double RAID_HP_REGEN_MULTIPLIER;
|
public static double RAID_HP_REGEN_MULTIPLIER;
|
||||||
@@ -2239,7 +2238,6 @@ public class Config
|
|||||||
AGGRO_DISTANCE_CHECK_INSTANCES = npcConfig.getBoolean("AggroDistanceCheckInstances", false);
|
AGGRO_DISTANCE_CHECK_INSTANCES = npcConfig.getBoolean("AggroDistanceCheckInstances", false);
|
||||||
AGGRO_DISTANCE_CHECK_RESTORE_LIFE = npcConfig.getBoolean("AggroDistanceCheckRestoreLife", true);
|
AGGRO_DISTANCE_CHECK_RESTORE_LIFE = npcConfig.getBoolean("AggroDistanceCheckRestoreLife", true);
|
||||||
GUARD_ATTACK_AGGRO_MOB = npcConfig.getBoolean("GuardAttackAggroMob", false);
|
GUARD_ATTACK_AGGRO_MOB = npcConfig.getBoolean("GuardAttackAggroMob", false);
|
||||||
ENABLE_GUARD_RETURN = npcConfig.getBoolean("EnableGuardReturn", false);
|
|
||||||
ALLOW_WYVERN_UPGRADER = npcConfig.getBoolean("AllowWyvernUpgrader", false);
|
ALLOW_WYVERN_UPGRADER = npcConfig.getBoolean("AllowWyvernUpgrader", false);
|
||||||
final String[] listPetRentNpc = npcConfig.getString("ListPetRentNpc", "30827").split(",");
|
final String[] listPetRentNpc = npcConfig.getString("ListPetRentNpc", "30827").split(",");
|
||||||
LIST_PET_RENT_NPC = new ArrayList<>(listPetRentNpc.length);
|
LIST_PET_RENT_NPC = new ArrayList<>(listPetRentNpc.length);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import java.util.concurrent.Future;
|
|||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.threads.ThreadPool;
|
import org.l2jmobius.commons.threads.ThreadPool;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.SkillData;
|
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
import org.l2jmobius.gameserver.enums.AIType;
|
import org.l2jmobius.gameserver.enums.AIType;
|
||||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||||
@@ -602,30 +601,29 @@ public class AttackableAI extends CreatureAI
|
|||||||
npc.getAttackByList().clear();
|
npc.getAttackByList().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a festival monster, then it remains in the same location.
|
||||||
|
// if (npc instanceof FestivalMonster)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a GuardInstance
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if ((npc instanceof Guard) && !npc.isWalker())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
if (Config.ENABLE_GUARD_RETURN && (npc.getSpawn() != null) && (Util.calculateDistance(npc, npc.getSpawn(), false, false) > 50) && /* !npc.isInsideZone(ZoneId.SIEGE) && */!npc.isCastingNow())
|
npc.setWalking();
|
||||||
{
|
npc.returnHome();
|
||||||
// Custom guard teleport to spawn.
|
return;
|
||||||
npc.clearAggroList();
|
|
||||||
npc.doCast(SkillData.getInstance().getSkill(1050, 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a festival monster, then it remains in the same location.
|
// Do not leave dead player
|
||||||
if (npc instanceof FestivalMonster)
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -681,7 +679,6 @@ public class AttackableAI extends CreatureAI
|
|||||||
// Order to the Monster to random walk (1/100)
|
// Order to the Monster to random walk (1/100)
|
||||||
else if ((npc.getSpawn() != null) && (Rnd.get(RANDOM_WALK_RATE) == 0) && npc.isRandomWalkingEnabled())
|
else if ((npc.getSpawn() != null) && (Rnd.get(RANDOM_WALK_RATE) == 0) && npc.isRandomWalkingEnabled())
|
||||||
{
|
{
|
||||||
|
|
||||||
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
|
for (Skill sk : npc.getTemplate().getAISkills(AISkillScope.BUFF))
|
||||||
{
|
{
|
||||||
if (cast(sk))
|
if (cast(sk))
|
||||||
|
|||||||
@@ -1688,6 +1688,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -4433,10 +4433,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof QuestGuard))
|
if ((Config.PATHFINDING > 0) && !(this instanceof QuestGuard))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -4476,31 +4476,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceId(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -4515,6 +4539,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -41,6 +41,7 @@ public class QuestGuard extends Guard
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.QuestGuard);
|
setInstanceType(InstanceType.QuestGuard);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import org.l2jmobius.gameserver.model.actor.Attackable;
|
|||||||
import org.l2jmobius.gameserver.model.actor.Creature;
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
import org.l2jmobius.gameserver.model.actor.Playable;
|
import org.l2jmobius.gameserver.model.actor.Playable;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Defender;
|
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
import org.l2jmobius.gameserver.model.actor.instance.GrandBoss;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
import org.l2jmobius.gameserver.model.actor.instance.Guard;
|
||||||
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
import org.l2jmobius.gameserver.model.actor.instance.Monster;
|
||||||
@@ -468,16 +467,23 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the mob should not return to spawn point
|
// Check if the mob should not return to spawn point
|
||||||
if (!npc.canReturnToSpawnPoint())
|
if (!npc.canReturnToSpawnPoint()
|
||||||
|
/* || npc.isReturningToSpawnPoint() */ ) // Commented because sometimes it stops movement.
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the actor is a guard
|
// Order this attackable to return to its spawn because there's no target to attack
|
||||||
if (((npc instanceof Guard) || (npc instanceof Defender)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
|
if (!npc.isWalker() && ((getTarget() == null) || getTarget().isInvisible() || (getTarget().isPlayer() && !getTarget().getActingPlayer().isAlikeDead())))
|
||||||
{
|
{
|
||||||
// Order to the GuardInstance to return to its home location because there's no target to attack
|
|
||||||
npc.returnHome();
|
npc.returnHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not leave dead player
|
||||||
|
if ((getTarget() != null) && getTarget().isPlayer() && getTarget().getActingPlayer().isAlikeDead())
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minions following leader
|
// Minions following leader
|
||||||
@@ -651,13 +657,19 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
Creature target = npc.getMostHated();
|
Creature target = npc.getMostHated();
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getTarget() != target)
|
if (getTarget() != target)
|
||||||
{
|
{
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if target is dead or if timeout is expired to stop this attack
|
// Check if target is dead or if timeout is expired to stop this attack
|
||||||
if ((target == null) || target.isAlikeDead())
|
if (target.isAlikeDead())
|
||||||
{
|
{
|
||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
npc.stopHating(target);
|
npc.stopHating(target);
|
||||||
|
|||||||
+1
@@ -1683,6 +1683,7 @@ public class Attackable extends Npc
|
|||||||
|
|
||||||
if (hasAI() && (getSpawn() != null))
|
if (hasAI() && (getSpawn() != null))
|
||||||
{
|
{
|
||||||
|
setReturningToSpawnPoint(true);
|
||||||
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-23
@@ -3469,10 +3469,10 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
// Movement checks.
|
// Movement checks.
|
||||||
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
if ((Config.PATHFINDING > 0) && !(this instanceof FriendlyNpc))
|
||||||
{
|
{
|
||||||
final double originalDistance = distance;
|
int originalX = x;
|
||||||
final int originalX = x;
|
int originalY = y;
|
||||||
final int originalY = y;
|
|
||||||
final int originalZ = z;
|
final int originalZ = z;
|
||||||
|
final double originalDistance = distance;
|
||||||
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
final int gtx = (originalX - World.WORLD_X_MIN) >> 4;
|
||||||
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
final int gty = (originalY - World.WORLD_Y_MIN) >> 4;
|
||||||
if (isOnGeodataPath())
|
if (isOnGeodataPath())
|
||||||
@@ -3512,31 +3512,55 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
{
|
{
|
||||||
// Path calculation -- overrides previous movement check
|
// Path calculation -- overrides previous movement check
|
||||||
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld(), isPlayer());
|
||||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
boolean found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
|
||||||
|
// If not found and it is an Attackable, attempt to find closest path to move location.
|
||||||
|
if (!found && isAttackable())
|
||||||
{
|
{
|
||||||
if (isPlayer() && !_isFlying && !isInWater)
|
int xMin = Math.min(curX, originalX);
|
||||||
|
int xMax = Math.max(curX, originalX);
|
||||||
|
int yMin = Math.min(curY, originalY);
|
||||||
|
int yMax = Math.max(curY, originalY);
|
||||||
|
final int maxDiff = Math.min(Math.max(xMax - xMin, yMax - yMin), 500);
|
||||||
|
xMin -= maxDiff;
|
||||||
|
xMax += maxDiff;
|
||||||
|
yMin -= maxDiff;
|
||||||
|
yMax += maxDiff;
|
||||||
|
int destinationX = 0;
|
||||||
|
int destinationY = 0;
|
||||||
|
double shortDistance = Double.MAX_VALUE;
|
||||||
|
double tempDistance;
|
||||||
|
List<AbstractNodeLoc> tempPath;
|
||||||
|
for (int sX = xMin; sX < xMax; sX += 500)
|
||||||
{
|
{
|
||||||
return;
|
for (int sY = yMin; sY < yMax; sY += 500)
|
||||||
|
{
|
||||||
|
tempDistance = Math.sqrt(Math.pow(sX - originalX, 2) + Math.pow(sY - originalY, 2));
|
||||||
|
if (tempDistance < shortDistance)
|
||||||
|
{
|
||||||
|
tempPath = PathFinding.getInstance().findPath(curX, curY, curZ, sX, sY, originalZ, getInstanceWorld(), false);
|
||||||
|
found = (tempPath != null) && (tempPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
shortDistance = tempDistance;
|
||||||
|
m.geoPath = tempPath;
|
||||||
|
destinationX = sX;
|
||||||
|
destinationY = sY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found = (m.geoPath != null) && (m.geoPath.size() > 1);
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
originalX = destinationX;
|
||||||
|
originalY = destinationY;
|
||||||
}
|
}
|
||||||
// if (!isPlayable() && !isMinion() && (Math.abs(z - curZ) > 140))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// if (isSummon() && !((Summon) this).getFollowStatus())
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
m.disregardingGeodata = true;
|
|
||||||
|
|
||||||
x = originalX;
|
|
||||||
y = originalY;
|
|
||||||
z = originalZ;
|
|
||||||
distance = originalDistance;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (found)
|
||||||
{
|
{
|
||||||
m.onGeodataPathIndex = 0; // on first segment
|
m.onGeodataPathIndex = 0; // On first segment.
|
||||||
m.geoPathGtx = gtx;
|
m.geoPathGtx = gtx;
|
||||||
m.geoPathGty = gty;
|
m.geoPathGty = gty;
|
||||||
m.geoPathAccurateTx = originalX;
|
m.geoPathAccurateTx = originalX;
|
||||||
@@ -3551,6 +3575,20 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
|||||||
sin = dy / distance;
|
sin = dy / distance;
|
||||||
cos = dx / distance;
|
cos = dx / distance;
|
||||||
}
|
}
|
||||||
|
else // No path found.
|
||||||
|
{
|
||||||
|
if (isPlayer() && !_isFlying && !isInWater)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.disregardingGeodata = true;
|
||||||
|
|
||||||
|
x = originalX;
|
||||||
|
y = originalY;
|
||||||
|
z = originalZ;
|
||||||
|
distance = originalDistance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ public class FriendlyNpc extends Attackable
|
|||||||
{
|
{
|
||||||
super(template);
|
super(template);
|
||||||
setInstanceType(InstanceType.FriendlyNpc);
|
setInstanceType(InstanceType.FriendlyNpc);
|
||||||
|
setCanReturnToSpawnPoint(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user