Цитата:
Сообщение от Pointer*Rage
Возникает бесконечная рекурсия:
movetolocation -> ai notify (arrived) -> movetolocation
Смотрите исходник, где-то косяк.
|
Спасибо за ответ.
Не вижу косяка
Код:
protected void moveToLocation(int x, int y, int z, int offset) {
// Get the Move Speed of the L2Charcater
float speed = getStat().getMoveSpeed();
if ((speed <= 0) || isMovementDisabled()) {
return;
}
// Get current position of the L2Character
final int curX = super.getX();
final int curY = super.getY();
final int curZ = super.getZ();
// Calculate distance (dx,dy) between current position and destination
// TODO: improve Z axis move/follow support when dx,dy are small compared to dz
double dx = (x - curX);
double dy = (y - curY);
double dz = (z - curZ);
double distance = Math.sqrt((dx * dx) + (dy * dy));
final boolean verticalMovementOnly = isFlying() && (distance == 0) && (dz != 0);
if (verticalMovementOnly) {
distance = Math.abs(dz);
}
// make water move short and use no geodata checks for swimming chars
// distance in a click can easily be over 3000
if ((Config.GEODATA > 0) && isInsideZone(ZoneId.WATER) && (distance > 700)) {
double divider = 700 / distance;
x = curX + (int) (divider * dx);
y = curY + (int) (divider * dy);
z = curZ + (int) (divider * dz);
dx = (x - curX);
dy = (y - curY);
dz = (z - curZ);
distance = Math.sqrt((dx * dx) + (dy * dy));
}
if (Config.DEBUG) {
_log.debug("distance to target:" + distance);
}
// Define movement angles needed
// ^
// | X (x,y)
// | /
// | /distance
// | /
// |/ angle
// X ---------->
// (curx,cury)
double cos;
double sin;
// Check if a movement offset is defined or no distance to go through
if ((offset > 0) || (distance < 1)) {
// approximation for moving closer when z coordinates are different
// TODO: handle Z axis movement better
offset -= Math.abs(dz);
if (offset < 5) {
offset = 5;
}
// If no distance to go through, the movement is canceled
if ((distance < 1) || ((distance - offset) <= 0)) {
if (Config.DEBUG) {
_log.debug("already in range, no movement needed.");
}
// Notify the AI that the L2Character is arrived at destination
getAI().notifyEvent(CtrlEvent.EVT_ARRIVED);
return;
}
// Calculate movement angles needed
sin = dy / distance;
cos = dx / distance;
distance -= (offset - 5); // due to rounding error, we have to move a bit closer to be in range
// Calculate the new destination with offset included
x = curX + (int) (distance * cos);
y = curY + (int) (distance * sin);
} else {
// Calculate movement angles needed
sin = dy / distance;
cos = dx / distance;
}
// Create and Init a MoveData object
MoveData m = new MoveData();
// GEODATA MOVEMENT CHECKS AND PATHFINDING
m.onGeodataPathIndex = -1; // Initialize not on geodata path
m.disregardingGeodata = false;
if ((Config.GEODATA > 0) && !isFlying() // flying chars not checked - even canSeeTarget doesn't work yet
&& (!isInsideZone(ZoneId.WATER) || isInsideZone(ZoneId.SIEGE)) // swimming also not checked unless in siege zone - but distance is limited
&& !(this instanceof L2NpcWalkerInstance)) // npc walkers not checked
{
final boolean isInVehicle = (this instanceof L2PcInstance) && (((L2PcInstance) this).getVehicle() != null);
if (isInVehicle) {
m.disregardingGeodata = true;
}
double originalDistance = distance;
int originalX = x;
int originalY = y;
int originalZ = z;
int gtx = (originalX - L2World.MAP_MIN_X) >> 4;
int gty = (originalY - L2World.MAP_MIN_Y) >> 4;
// Movement checks:
// when geodata == 2, for all characters except mobs returning home (could be changed later to teleport if pathfinding fails)
// when geodata == 1, for l2playableinstance and l2riftinstance only
if (((Config.GEODATA == 2) && !((this instanceof L2Attackable) && ((L2Attackable) this).isReturningToSpawnPoint())) || ((this instanceof L2PcInstance) && !(isInVehicle && (distance > 1500))) || ((this instanceof L2Summon) && !(getAI().getIntention() == CtrlIntention.FOLLOW)) // assuming
// intention_follow
// only when
// following owner
|| isAfraid() || (this instanceof L2RiftInvaderInstance)) {
if (isOnGeodataPath()) {
try {
if ((gtx == _move.geoPathGtx) && (gty == _move.geoPathGty)) {
return;
}
_move.onGeodataPathIndex = -1; // Set not on geodata path
} catch (NullPointerException e) {
// nothing
}
}
if ((curX < L2World.MAP_MIN_X) || (curX > L2World.MAP_MAX_X) || (curY < L2World.MAP_MIN_Y) || (curY > L2World.MAP_MAX_Y)) {
// Temporary fix for character outside world region errors
_log.warn("Character " + getName() + " outside world area, in coordinates x:" + curX + " y:" + curY);
getAI().setIntention(CtrlIntention.IDLE);
if (this instanceof L2PcInstance) {
((L2PcInstance) this).logout();
} else if (this instanceof L2Summon) {
return; // prevention when summon get out of world coords, player will not loose him, unsummon handled from pcinstance
} else {
onDecay();
}
return;
}
Location destiny = GeoData.getInstance().moveCheck(curX, curY, curZ, x, y, z);
// location different if destination wasn't reached (or just z coord is different)
x = destiny.getX();
y = destiny.getY();
z = destiny.getZ();
dx = x - curX;
dy = y - curY;
dz = z - curZ;
distance = verticalMovementOnly ? Math.abs(dz * dz) : Math.sqrt((dx * dx) + (dy * dy));
}
// Pathfinding checks. Only when geodata setting is 2, the LoS check gives shorter result
// than the original movement was and the LoS gives a shorter distance than 2000
// This way of detecting need for pathfinding could be changed.
if ((Config.GEODATA == 2) && ((originalDistance - distance) > 30) && (distance < 2000) && !isAfraid()) {
// Path calculation -- overrides previous movement check
if (((this instanceof L2Playable) && !isInVehicle) || isMinion() || isInCombat()) {
m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, this instanceof L2Playable);
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
{
// * Even though there's no path found (remember geonodes aren't perfect),
// the mob is attacking and right now we set it so that the mob will go
// after target anyway, is dz is small enough.
// * With cellpathfinding this approach could be changed but would require taking
// off the geonodes and some more checks.
// * Summons will follow their masters no matter what.
// * Currently minions also must move freely since L2AttackableAI commands
// them to move along with their leader
if ((this instanceof L2PcInstance) || (!(this instanceof L2Playable) && !isMinion() && (Math.abs(z - curZ) > 140)) || ((this instanceof L2Summon) && !((L2Summon) this).getFollowStatus())) {
getAI().setIntention(CtrlIntention.IDLE);
return;
}
m.disregardingGeodata = true;
x = originalX;
y = originalY;
z = originalZ;
distance = originalDistance;
} else {
m.onGeodataPathIndex = 0; // on first segment
m.geoPathGtx = gtx;
m.geoPathGty = gty;
m.geoPathAccurateTx = originalX;
m.geoPathAccurateTy = originalY;
x = m.geoPath.get(m.onGeodataPathIndex).getX();
y = m.geoPath.get(m.onGeodataPathIndex).getY();
z = m.geoPath.get(m.onGeodataPathIndex).getZ();
// check for doors in the route
if (DoorTable.getInstance().checkIfDoorsBetween(curX, curY, curZ, x, y, z)) {
m.geoPath = null;
getAI().setIntention(CtrlIntention.IDLE);
return;
}
for (int i = 0; i < (m.geoPath.size() - 1); i++) {
if (DoorTable.getInstance().checkIfDoorsBetween(m.geoPath.get(i), m.geoPath.get(i + 1))) {
m.geoPath = null;
getAI().setIntention(CtrlIntention.IDLE);
return;
}
}
dx = x - curX;
dy = y - curY;
dz = z - curZ;
distance = verticalMovementOnly ? Math.abs(dz * dz) : Math.sqrt((dx * dx) + (dy * dy));
sin = dy / distance;
cos = dx / distance;
}
}
}
// If no distance to go through, the movement is canceled
if ((distance < 1) && ((Config.GEODATA == 2) || (this instanceof L2Playable) || (this instanceof L2RiftInvaderInstance) || isAfraid())) {
if (this instanceof L2Summon) {
((L2Summon) this).setFollowStatus(false);
}
getAI().setIntention(CtrlIntention.IDLE);
return;
}
}
// Apply Z distance for flying or swimming for correct timing calculations
if ((isFlying() || isInsideZone(ZoneId.WATER)) && !verticalMovementOnly) {
distance = Math.sqrt((distance * distance) + (dz * dz));
}
// Caclulate the Nb of ticks between the current position and the destination
// One tick added for rounding reasons
int ticksToMove = 1 + (int) ((GameTimeController.TICKS_PER_SECOND * distance) / speed);
m._xDestination = x;
m._yDestination = y;
m._zDestination = z; // this is what was requested from client
// Calculate and set the heading of the L2Character
m._heading = 0; // initial value for coordinate sync
// Does not broke heading on vertical movements
if (!verticalMovementOnly) {
setHeading(Util.calculateHeadingFrom(cos, sin));
}
if (Config.DEBUG) {
_log.debug("dist:" + distance + "speed:" + speed + " ttt:" + ticksToMove + " heading:" + getHeading());
}
m._moveStartTime = GameTimeController.getGameTicks();
// Set the L2Character _move object to MoveData object
_move = m;
// Add the L2Character to movingObjects of the GameTimeController
GameTimeController.getInstance().registerMovingObject(this);
// Create a task to notify the AI that L2Character arrives at a check point of the movement
if ((ticksToMove * GameTimeController.MILLIS_IN_TICK) > 3000) {
ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(CtrlEvent.EVT_ARRIVED_REVALIDATE), 2000);
}
}