 | 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[randomDir];
int newY = _pc.getY() + L1AutoMoveService.HEADING_TABLE_Y[randomDir];
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);
}
}
| |