skyalcon 發表於 2026-1-13 18:30

L1AutoHuntHandler

package l1j.server.server.model.Instance;

import l1j.server.server.model.L1Object;
import l1j.server.server.model.L1World;
import l1j.server.server.serverpackets.S_NPCPack;

import java.util.ArrayList;
import java.util.List;

public class L1AutoHuntHandler implements Runnable {
    private final L1PcInstance _pc;
    private L1MonsterInstance _target = null;
    private int _stuckCounter = 0;
    private int _lastX = -1;
    private int _lastY = -1;

    public L1AutoHuntHandler(L1PcInstance pc) {
      this._pc = pc;
      this._lastX = pc.getX();
      this._lastY = pc.getY();
    }

    @Override
    public void run() {
      try {
            while (_pc.isAutoHunting()
                  && _pc.getNetConnection() != null
                  && !_pc.isDead()) {

                // 1. 檢查自動喝水
                checkAutoPotion();

                // 2. 更新視野內的怪物顯示
                updateMonsterVisibility();

                // 3. 檢查目標有效性
                if (!isValidTarget(_target)) {
                  _target = findBestTarget();
                  if (_target == null) {
                        Thread.sleep(300);
                        continue;
                  }
                }

                // 4. 執行戰鬥邏輯
                executeCombat(_target);

                // 5. 防卡點檢測
                checkStuck();

                // 6. 頻率控制
                Thread.sleep(300);
            }
      } catch (Exception e) {
            e.printStackTrace();
      } finally {
            // 清理資源
            cleanup();
      }
    }

    /**
   * 執行戰鬥邏輯
   */
    private void executeCombat(L1MonsterInstance target) {
      if (target == null || target.isDead()) {
            _target = null;
            return;
      }

      int dist = calculateDistance(_pc, target);

      if (dist > 1) {
            // 移動到目標
            boolean moved = moveToTarget(target);

            if (!moved) {
                // 移動失敗,重新選擇目標
                _target = null;
            }
      } else {
            // 進行攻擊
            attackTarget(target);
      }
    }

    /**
   * 移動到目標
   */
    private boolean moveToTarget(L1MonsterInstance target) {
      // 檢查目標是否在視野內
      if (!isInScreen(target)) {
            _target = null;
            return false;
      }

      // 記錄移動前位置
      int oldX = _pc.getX();
      int oldY = _pc.getY();

      // 執行移動
      L1AutoMoveService.moveToTarget(_pc, target.getX(), target.getY());

      // 檢查是否移動成功
      if (oldX != _pc.getX() || oldY != _pc.getY()) {
            _stuckCounter = 0; // 重置卡點計數器
            return true;
      } else {
            _stuckCounter++;
            return false;
      }
    }

    /**
   * 攻擊目標
   */
    private void attackTarget(L1MonsterInstance target) {
      try {
            // 設置面向目標的方向
            int dir = _pc.targetDirection(target.getX(), target.getY());
            _pc.setHeading(dir);

            // 執行攻擊
            target.onAction(_pc);

      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    /**
   * 尋找最佳目標
   */
    private L1MonsterInstance findBestTarget() {
      List<L1MonsterInstance> visibleMonsters = new ArrayList<>();

      // 獲取所有可見的怪物
      for (L1Object obj : L1World.getInstance().getVisibleObjects(_pc, 15)) {
            if (obj instanceof L1MonsterInstance) {
                L1MonsterInstance monster = (L1MonsterInstance) obj;

                // 檢查怪物是否有效
                if (isValidMonster(monster)) {
                  visibleMonsters.add(monster);
                }
            }
      }

      if (visibleMonsters.isEmpty()) {
            return null;
      }

      // 選擇最近的怪物
      L1MonsterInstance bestTarget = null;
      int minDistance = Integer.MAX_VALUE;

      for (L1MonsterInstance monster : visibleMonsters) {
            int distance = calculateDistance(_pc, monster);

            if (distance < minDistance) {
                minDistance = distance;
                bestTarget = monster;
            }
      }

      return bestTarget;
    }

    /**
   * 檢查目標有效性
   */
    private boolean isValidTarget(L1MonsterInstance target) {
      if (target == null) return false;

      return !target.isDead() &&
               target.getMapId() == _pc.getMapId() &&
               isInScreen(target) &&
               calculateDistance(_pc, target) <= 15;
    }

    /**
   * 檢查怪物是否有效
   */
    private boolean isValidMonster(L1MonsterInstance monster) {
      if (monster == null) return false;

      return !monster.isDead() &&
               !monster.isInvisble() &&
               monster.getMapId() == _pc.getMapId() &&
               monster.getCurrentHp() > 0 &&
               !monster.isParalyzed();
    }

    /**
   * 更新怪物顯示
   */
    private void updateMonsterVisibility() {
      try {
            // 獲取當前視野範圍內的怪物
            for (L1Object obj : L1World.getInstance().getVisibleObjects(_pc, 15)) {
                if (obj instanceof L1MonsterInstance) {
                  L1MonsterInstance monster = (L1MonsterInstance) obj;

                  // 如果怪物在畫面內,確保發送顯示封包
                  if (isInScreen(monster)) {
                        _pc.sendPackets(new S_NPCPack(monster));
                  }
                }
            }
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    /**
   * 檢查是否在畫面內
   */
    private boolean isInScreen(L1MonsterInstance monster) {
      if (monster == null) return false;

      int screenRange = 15; // 天堂標準視野範圍

      int dx = Math.abs(_pc.getX() - monster.getX());
      int dy = Math.abs(_pc.getY() - monster.getY());

      return dx <= screenRange && dy <= screenRange;
    }

    /**
   * 計算距離(使用 Chebyshev 距離,天堂的標準算法)
   */
    private int calculateDistance(L1PcInstance pc, L1MonsterInstance monster) {
      int dx = Math.abs(pc.getX() - monster.getX());
      int dy = Math.abs(pc.getY() - monster.getY());
      return Math.max(dx, dy);
    }

    /**
   * 防卡點檢測
   */
    private void checkStuck() {
      // 檢查是否卡在同一個位置太久
      if (_pc.getX() == _lastX && _pc.getY() == _lastY) {
            _stuckCounter++;

            // 如果連續卡住超過5次,嘗試隨機移動
            if (_stuckCounter > 5) {
                tryRandomMove();
                _stuckCounter = 0;
            }
      } else {
            _stuckCounter = 0;
            _lastX = _pc.getX();
            _lastY = _pc.getY();
      }
    }

    /**
   * 嘗試隨機移動(解卡)
   */
    private void tryRandomMove() {
      try {
            int randomDir = (int) (Math.random() * 8);
            int newX = _pc.getX() + L1AutoMoveService.HEADING_TABLE_X;
            int newY = _pc.getY() + L1AutoMoveService.HEADING_TABLE_Y;

            if (_pc.getMap().isPassable(newX, newY, _pc)) {
                L1AutoMoveService.setLocation(_pc, newX, newY, randomDir);
            }
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    /**
   * 檢查自動喝水
   */
    private void checkAutoPotion() {
      try {
            // HP 低於 30% 時自動喝水
            int currentHp = _pc.getCurrentHp();
            int maxHp = _pc.getMaxHp();

            if (currentHp < maxHp * 0.3) {
                // 這裡應該調用使用藥水的邏輯
                // _pc.useItem(L1ItemId.POTION_OF_HEALING);
            }

            // MP 低於 20% 時自動喝水
            int currentMp = _pc.getCurrentMp();
            int maxMp = _pc.getMaxMp();

            if (currentMp < maxMp * 0.2) {
                // _pc.useItem(L1ItemId.POTION_OF_MANA);
            }

      } catch (Exception e) {
            // 忽略錯誤,不影響主要邏輯
      }
    }

    /**
   * 清理資源
   */
    private void cleanup() {
      _target = null;
      _stuckCounter = 0;
      _pc.setAutoHunting(false);
    }
}

頁: [1]
查看完整版本: L1AutoHuntHandler