📄 l1npcinstance.java
字号:
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* http://www.gnu.org/copyleft/gpl.html
*/
package l1j.server.server.model.Instance;
//
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import l1j.server.Config;
import l1j.server.server.ActionCodes;
import l1j.server.server.GeneralThreadPool;
import l1j.server.server.datatables.NpcChatTable;
import l1j.server.server.datatables.NpcTable;
import l1j.server.server.model.L1Attack;
import l1j.server.server.model.L1Character;
import l1j.server.server.model.L1GroundInventory;
import l1j.server.server.model.L1HateList;
import l1j.server.server.model.L1Inventory;
import l1j.server.server.model.L1Magic;
import l1j.server.server.model.L1MobGroupInfo;
import l1j.server.server.model.L1MobSkillUse;
import l1j.server.server.model.L1NpcRegenerationTimer;
import l1j.server.server.model.L1NpcChatTimer;
import l1j.server.server.model.L1Object;
import l1j.server.server.model.L1Spawn;
import l1j.server.server.model.L1World;
import l1j.server.server.model.map.L1Map;
import l1j.server.server.model.map.L1WorldMap;
import l1j.server.server.model.skill.L1SkillId;
import l1j.server.server.model.skill.L1SkillUse;
import l1j.server.server.serverpackets.S_ChangeShape;
import l1j.server.server.serverpackets.S_RemoveObject;
import l1j.server.server.serverpackets.S_DoActionGFX;
import l1j.server.server.serverpackets.S_MoveCharPacket;
import l1j.server.server.serverpackets.S_NPCPack;
import l1j.server.server.serverpackets.S_SkillHaste;
import l1j.server.server.serverpackets.S_SkillSound;
import l1j.server.server.templates.L1Npc;
import l1j.server.server.templates.L1NpcChat;
import l1j.server.server.types.Point;
import l1j.server.server.utils.TimerPool;
import static l1j.server.server.model.item.L1ItemId.*;
public class L1NpcInstance extends L1Character {
private static final long serialVersionUID = 1L;
public static final int MOVE_SPEED = 0;
public static final int ATTACK_SPEED = 1;
public static final int MAGIC_SPEED = 2;
public static final int HIDDEN_STATUS_NONE = 0;
public static final int HIDDEN_STATUS_SINK = 1;
public static final int HIDDEN_STATUS_FLY = 2;
public static final int CHAT_TIMING_APPEARANCE = 0;
public static final int CHAT_TIMING_DEAD = 1;
public static final int CHAT_TIMING_HIDE = 2;
public static final int CHAT_TIMING_GAME_TIME = 3;
private static Logger _log = Logger
.getLogger(L1NpcInstance.class.getName());
private L1Npc _npcTemplate;
private L1Spawn _spawn;
private int _spawnNumber; // L1Spawnで管理されているナンバー
private int _petcost; // ペットになったときのコスト
public L1Inventory _inventory = new L1Inventory();
private L1MobSkillUse mobSkill;
private static Random _random = new Random();
// 対象を初めて発見したとき。(テレポート用)
private boolean firstFound = true;
// 経路探索範囲(半径) ※上げすぎ注意!!
public static int courceRange = 15;
// 吸われたMP
private int _drainedMana = 0;
// 休憩
private boolean _rest = false;
// ランダム移動時の距離と方向
private int _randomMoveDistance = 0;
private int _randomMoveDirection = 0;
// ■■■■■■■■■■■■■ AI関連 ■■■■■■■■■■■
interface NpcAI {
public void start();
}
protected void startAI() {
if (Config.NPCAI_IMPLTYPE == 1) {
new NpcAITimerImpl().start();
} else if (Config.NPCAI_IMPLTYPE == 2) {
new NpcAIThreadImpl().start();
} else {
new NpcAITimerImpl().start();
}
}
/**
* マルチ(コア)プロセッサをサポートする為のタイマープール。 AIの実装タイプがタイマーの場合に使用される。
*/
private static final TimerPool _timerPool = new TimerPool(4);
class NpcAITimerImpl extends TimerTask implements NpcAI {
/**
* 死亡処理の終了を待つタイマー
*/
private class DeathSyncTimer extends TimerTask {
private void schedule(int delay) {
_timerPool.getTimer().schedule(new DeathSyncTimer(), delay);
}
@Override
public void run() {
if (isDeathProcessing()) {
schedule(getSleepTime());
return;
}
allTargetClear();
setAiRunning(false);
}
}
@Override
public void start() {
setAiRunning(true);
_timerPool.getTimer().schedule(NpcAITimerImpl.this, 0);
}
private void stop() {
mobSkill.resetAllSkillUseCount();
_timerPool.getTimer().schedule(new DeathSyncTimer(), 0); // 死亡同期を開始
}
// 同じインスタンスをTimerへ登録できない為、苦肉の策。
private void schedule(int delay) {
_timerPool.getTimer().schedule(new NpcAITimerImpl(), delay);
}
@Override
public void run() {
try {
if (notContinued()) {
stop();
return;
}
// XXX 同期がとても怪しげな麻痺判定
if (0 < _paralysisTime) {
schedule(_paralysisTime);
_paralysisTime = 0;
setParalyzed(false);
return;
} else if (isParalyzed() || isSleeped()) {
schedule(200);
return;
}
if (!AIProcess()) { // AIを続けるべきであれば、次の実行をスケジュールし、終了
schedule(getSleepTime());
return;
}
stop();
} catch (Exception e) {
_log.log(Level.WARNING, "NpcAIで例外が発生しました。", e);
}
}
private boolean notContinued() {
return _destroyed || isDead() || getCurrentHp() <= 0
|| getHiddenStatus() != HIDDEN_STATUS_NONE;
}
}
class NpcAIThreadImpl implements Runnable, NpcAI {
@Override
public void start() {
GeneralThreadPool.getInstance().execute(NpcAIThreadImpl.this);
}
@Override
public void run() {
try {
setAiRunning(true);
while (!_destroyed && !isDead() && getCurrentHp() > 0
&& getHiddenStatus() == HIDDEN_STATUS_NONE) {
/*
* if (_paralysisTime > 0) { try {
* Thread.sleep(_paralysisTime); } catch (Exception
* exception) { break; } finally { setParalyzed(false);
* _paralysisTime = 0; } }
*/
while (isParalyzed() || isSleeped()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
setParalyzed(false);
}
}
if (AIProcess()) {
break;
}
try {
// 指定時間分スレッド停止
Thread.sleep(getSleepTime());
} catch (Exception e) {
break;
}
}
mobSkill.resetAllSkillUseCount();
do {
try {
Thread.sleep(getSleepTime());
} catch (Exception e) {
break;
}
} while (isDeathProcessing());
allTargetClear();
setAiRunning(false);
} catch (Exception e) {
_log.log(Level.WARNING, "NpcAIで例外が発生しました。", e);
}
}
}
// AIの処理 (返り値はAI処理を終了するかどうか)
private boolean AIProcess() {
setSleepTime(300);
checkTarget();
if (_target == null && _master == null) {
// 空っぽの場合はターゲットを探してみる
// (主人がいる場合は自分でターゲットを探さない)
searchTarget();
}
onItemUse();
if (_target == null) {
// ターゲットがいない場合
checkTargetItem();
if (isPickupItem() && _targetItem == null) {
// アイテム拾う子の場合はアイテムを探してみる
searchTargetItem();
}
if (_targetItem == null) {
if (noTarget()) {
return true;
}
} else {
// onTargetItem();
L1Inventory groundInventory = L1World.getInstance()
.getInventory(_targetItem.getX(), _targetItem.getY(),
_targetItem.getMapId());
if (groundInventory.checkItem(_targetItem.getItemId())) {
onTargetItem();
} else {
_targetItemList.remove(_targetItem);
_targetItem = null;
setSleepTime(1000);
return false;
}
}
} else { // ターゲットがいる場合
if (getHiddenStatus() == HIDDEN_STATUS_NONE) {
onTarget();
} else {
return true;
}
}
return false; // AI処理続行
}
// アイテム使用処理(Typeによって結構違うのでオーバライドで実装)
public void onItemUse() {
}
// ターゲットを探す(Typeによって結構違うのでオーバライドで実装)
public void searchTarget() {
}
// 有效なターゲットか確認及び次のターゲットを設定
public void checkTarget() {
if (_target == null
|| _target.getMapId() != getMapId()
|| _target.getCurrentHp() <= 0
|| _target.isDead()
|| (_target.isInvisble() && !getNpcTemplate().is_agrocoi() && !_hateList
.containsKey(_target))) {
if (_target != null) {
tagertClear();
}
if (!_hateList.isEmpty()) {
_target = _hateList.getMaxHateCharacter();
checkTarget();
}
}
}
// ヘイトの設定
public void setHate(L1Character cha, int hate) {
if (cha != null && cha.getId() != getId()) {
if (!isFirstAttack() && hate != 0) {
// hate += 20; // FAヘイト
hate += getMaxHp() / 10; // FAヘイト
setFirstAttack(true);
}
_hateList.add(cha, hate);
_dropHateList.add(cha, hate);
_target = _hateList.getMaxHateCharacter();
checkTarget();
}
}
// リンクの設定
public void setLink(L1Character cha) {
}
// 仲間意識によりアクティブになるNPCの検索(攻撃者がプレイヤーのみ有效)
public void serchLink(L1PcInstance targetPlayer, int family) {
List<L1Object> targetKnownObjects = targetPlayer.getKnownObjects();
for (Object knownObject : targetKnownObjects) {
if (knownObject instanceof L1NpcInstance) {
L1NpcInstance npc = (L1NpcInstance) knownObject;
if (npc.getNpcTemplate().get_agrofamily() > 0) {
// 仲間に対してアクティブになる場合
if (npc.getNpcTemplate().get_agrofamily() == 1) {
// 同種族に対してのみ仲間意識
if (npc.getNpcTemplate().get_family() == family) {
npc.setLink(targetPlayer);
}
} else {
// 全てのNPCに対して仲間意識
npc.setLink(targetPlayer);
}
}
L1MobGroupInfo mobGroupInfo = getMobGroupInfo();
if (mobGroupInfo != null) {
if (getMobGroupId() != 0
&& getMobGroupId() == npc.getMobGroupId()) { // 同じグループ
npc.setLink(targetPlayer);
}
}
}
}
}
// ターゲットがいる場合の処理
public void onTarget() {
setActived(true);
_targetItemList.clear();
_targetItem = null;
L1Character target = _target; // ここから先は_targetが変わると影響出るので別領域に参照確保
if (getAtkspeed() == 0) // 逃げるキャラ
{
if (getPassispeed() > 0) // 移動できるキャラ
{
int escapeDistance = 15;
if (hasSkillEffect(40) == true) {
escapeDistance = 1;
}
if (getLocation().getTileLineDistance(target.getLocation()) > escapeDistance) {
// ターゲットから逃げるの終了
tagertClear();
} else { // ターゲットから逃げる
int dir = targetReverseDirection(target.getX(), target
.getY());
dir = checkObject(getX(), getY(), getMapId(), dir);
setDirectionMove(dir);
setSleepTime(calcSleepTime(getPassispeed(), MOVE_SPEED));
}
}
} else { // 逃げないキャラ
boolean isSkillUse = false;
isSkillUse = mobSkill.skillUse(target);
if (isSkillUse == true) {
setSleepTime(calcSleepTime(mobSkill.getSleepTime(),
MAGIC_SPEED));
return;
}
if (isAttackPosition(target.getX(), target.getY(), getNpcTemplate()
.get_ranged())) {
// 攻撃可能位置
setHeading(targetDirection(target.getX(), target.getY()));
attackTarget(target);
} else {
// 攻撃不可能位置
if (getPassispeed() > 0) {
// 移動できるキャラ
int distance = getLocation().getTileDistance(
target.getLocation());
if (firstFound == true && getNpcTemplate().is_teleport()
&& distance > 3 && distance < 15) {
if (nearTeleport(target.getX(), target.getY()) == true) {
firstFound = false;
return;
}
}
if (getNpcTemplate().is_teleport()
&& 20 > _random.nextInt(100)
&& getCurrentMp() >= 10 && distance > 6
&& distance < 15) { // テレポート移動
if (nearTeleport(target.getX(), target.getY()) == true) {
return;
}
}
int dir = moveDirection(target.getX(), target.getY());
if (dir == -1) {
tagertClear();
} else {
setDirectionMove(dir);
setSleepTime(calcSleepTime(getPassispeed(),
MOVE_SPEED));
}
} else {
// 移動できないキャラ(ターゲットから排除、PTのときドロップチャンスがリセットされるけどまぁ自業自得)
tagertClear();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -