L1AutoMoveService
package l1j.server.server.model.Instance;import l1j.server.server.ClientThread;
import l1j.server.server.clientpackets.C_MoveChar;
import l1j.server.server.serverpackets.S_MoveCharPacket;
import l1j.server.server.serverpackets.S_NPCPack;
public class L1AutoMoveService {
// 8個方向的位置偏移量(必須與 C_MoveChar.java 中的一致)
private static final int[] HEADING_TABLE_X = {0, 1, 1, 1, 0, -1, -1, -1};
private static final int[] HEADING_TABLE_Y = {-1, -1, 0, 1, 1, 1, 0, -1};
/**
* 台灣版移動服務 - 修正怪物消失問題
* 台灣版協議:客戶端只發送方向,不發送目標座標
*/
public static void moveToTarget(L1PcInstance pc, int targetX, int targetY) {
if (pc == null || pc.isDead() || pc.isParalyzed() || pc.isTeleport() || pc.isInvisble()) {
return;
}
// 1. 計算最佳移動方向
int dir = calculateBestDirection(pc, targetX, targetY);
if (dir == -1) {
return; // 沒有可通行的方向
}
// 2. 計算移動後的位置
int nextX = pc.getX() + HEADING_TABLE_X;
int nextY = pc.getY() + HEADING_TABLE_Y;
// 3. 檢查是否可以通行
if (!pc.getMap().isPassable(nextX, nextY, pc)) {
// 嘗試其他方向
dir = findAlternativeDirection(pc, dir);
if (dir == -1) {
return;
}
nextX = pc.getX() + HEADING_TABLE_X;
nextY = pc.getY() + HEADING_TABLE_Y;
}
// 4. 【關鍵】保存移動前的座標,用於視野更新
int oldX = pc.getX();
int oldY = pc.getY();
// 5. 【關鍵】立即更新伺服器端的位置
pc.getLocation().set(nextX, nextY);
pc.setHeading(dir);
// 6. 【關鍵】發送移動封包給客戶端(台灣版格式)
sendTaiwanMovePacket(pc, dir);
// 7. 【關鍵】廣播位置更新給其他玩家
broadcastPositionUpdate(pc);
// 8. 【關鍵】如果位置改變了,更新視野內的物件
if (oldX != nextX || oldY != nextY) {
updateVisibleObjects(pc, oldX, oldY);
}
}
/**
* 計算最佳移動方向(考慮障礙物)
*/
private static int calculateBestDirection(L1PcInstance pc, int targetX, int targetY) {
int currentX = pc.getX();
int currentY = pc.getY();
// 計算原始方向
int idealDir = calculateDirection(currentX, currentY, targetX, targetY);
// 檢查原始方向是否可通行
int nextX = currentX + HEADING_TABLE_X;
int nextY = currentY + HEADING_TABLE_Y;
if (pc.getMap().isPassable(nextX, nextY, pc)) {
return idealDir;
}
// 如果不可通行,尋找最佳替代方向
return findAlternativeDirection(pc, idealDir);
}
/**
* 計算基本方向
*/
private static int calculateDirection(int currentX, int currentY, int targetX, int targetY) {
int dx = targetX - currentX;
int dy = targetY - currentY;
// 標準的8方向計算
if (dx == 0 && dy < 0) return 0; // 上
if (dx > 0 && dy < 0) return 1; // 右上
if (dx > 0 && dy == 0) return 2; // 右
if (dx > 0 && dy > 0) return 3; // 右下
if (dx == 0 && dy > 0) return 4; // 下
if (dx < 0 && dy > 0) return 5; // 左下
if (dx < 0 && dy == 0) return 6; // 左
if (dx < 0 && dy < 0) return 7; // 左上
return 0; // 預設向上
}
/**
* 尋找替代方向
*/
private static int findAlternativeDirection(L1PcInstance pc, int originalDir) {
// 優先嘗試的方向順序:原始方向 -> 左右45度 -> 左右90度 -> 反向
int[] tryOrder = {
originalDir, // 原始方向
(originalDir + 1) % 8, // 右45度
(originalDir + 7) % 8, // 左45度
(originalDir + 2) % 8, // 右90度
(originalDir + 6) % 8, // 左90度
(originalDir + 4) % 8, // 相反方向
(originalDir + 3) % 8, // 右135度
(originalDir + 5) % 8 // 左135度
};
for (int dir : tryOrder) {
int nextX = pc.getX() + HEADING_TABLE_X;
int nextY = pc.getY() + HEADING_TABLE_Y;
if (pc.getMap().isPassable(nextX, nextY, pc)) {
return dir;
}
}
return -1; // 沒有可通行的方向
}
/**
* 【關鍵】發送台灣版移動封包
* 台灣版:只發送方向,不發送目標座標
*/
private static void sendTaiwanMovePacket(L1PcInstance pc, int dir) {
ClientThread client = pc.getNetConnection();
if (client == null) {
return;
}
try {
// 台灣版封包結構:只需要發送方向
byte[] data = new byte;
// 【關鍵】台灣版加密:方向 XOR 0x49
int encryptedDir = dir ^ 0x49;
// 填充封包(台灣版C_MoveChar會忽略座標,只讀取方向)
data = (byte) encryptedDir;
// 發送移動封包
new C_MoveChar(data, client);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 廣播位置更新
*/
private static void broadcastPositionUpdate(L1PcInstance pc) {
try {
S_MoveCharPacket packet = new S_MoveCharPacket(pc);
// 根據玩家狀態決定如何廣播
if (pc.isGmInvis() || pc.isGhost()) {
// GM隱身或鬼魂模式不廣播
} else if (pc.isInvisble()) {
pc.broadcastPacketForFindInvis(packet, true);
} else {
pc.broadcastPacket(packet);
}
// 更新自己的位置顯示
pc.sendPackets(packet);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 【關鍵】更新可見物件 - 解決怪物消失問題
*/
private static void updateVisibleObjects(L1PcInstance pc, int oldX, int oldY) {
try {
// 獲取玩家周圍的所有物件
java.util.List<l1j.server.server.model.L1Object> allObjects =
l1j.server.server.model.L1World.getInstance().getVisibleObjects(pc, 20); // 稍微擴大範圍
// 重新發送所有可見怪物的顯示封包
for (l1j.server.server.model.L1Object obj : allObjects) {
if (obj instanceof L1MonsterInstance) {
L1MonsterInstance monster = (L1MonsterInstance) obj;
// 檢查怪物是否有效且在畫面內
if (isValidMonster(monster) && isInScreen(pc, monster)) {
// 【關鍵】重新發送怪物顯示封包
S_NPCPack packet = new S_NPCPack(monster);
pc.sendPackets(packet);
// 如果怪物對其他玩家也可見,也更新他們
if (!monster.isDead() && !monster.isInvisble()) {
monster.broadcastPacket(packet);
}
}
}
}
// 【重要】觸發視野更新
pc.updateObject();
// 更新隱身偵測
pc.broadcastPacketForFindInvisibility();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 檢查怪物是否有效
*/
private static boolean isValidMonster(L1MonsterInstance monster) {
return monster != null &&
!monster.isDead() &&
!monster.isInvisble() &&
monster.getCurrentHp() > 0;
}
/**
* 檢查物件是否在畫面內
*/
private static boolean isInScreen(L1PcInstance pc, l1j.server.server.model.L1Object obj) {
if (pc == null || obj == null) return false;
int screenRange = 16; // 稍微擴大範圍確保顯示
int dx = Math.abs(pc.getX() - obj.getX());
int dy = Math.abs(pc.getY() - obj.getY());
return dx <= screenRange && dy <= screenRange;
}
/**
* 直接設定位置(用於瞬移或位置修正)
*/
public static void setLocation(L1PcInstance pc, int x, int y, int heading) {
if (pc == null) return;
// 保存舊位置
int oldX = pc.getX();
int oldY = pc.getY();
// 更新位置
pc.getLocation().set(x, y);
pc.setHeading(heading);
// 廣播位置更新
broadcastPositionUpdate(pc);
// 更新視野物件
if (oldX != x || oldY != y) {
updateVisibleObjects(pc, oldX, oldY);
}
}
/**
* 計算兩點之間的 Chebyshev 距離(天堂標準)
*/
public static int calculateDistance(int x1, int y1, int x2, int y2) {
int dx = Math.abs(x1 - x2);
int dy = Math.abs(y1 - y2);
return Math.max(dx, dy);
}
/**
* 檢查是否可以移動到目標
*/
public static boolean canMoveTo(L1PcInstance pc, int targetX, int targetY) {
int dir = calculateDirection(pc.getX(), pc.getY(), targetX, targetY);
int nextX = pc.getX() + HEADING_TABLE_X;
int nextY = pc.getY() + HEADING_TABLE_Y;
return pc.getMap().isPassable(nextX, nextY, pc);
}
}
頁:
[1]