---------------こっこAI⊂(*'ω')⊃ Ver4.82 07/07/03--------------------------------------------- -- 機能詳細,設定マニュアルなどは以下のURLから確認してください. -- http://privatemoon.sakura.ne.jp/alc/cocco_manual.html ----------------------------Editing by 冠月ユウ----------------------------------------------------- InitialFlag = true function Initialize() TraceAI("----- Initialize ----- ") ------------------------------*-*-*-*-*-*-*-*-*-------------------------------- -- グローバル変数(カスタム) -- 数値を変更することで設定を変えられるものにはコメント先頭に●が付いています -- それ以外の変数は基本的に変更しないでください! ------------------------------------------------------------------------------- MyOwner = GetV(V_OWNER,MyID) -- 主人のID取得 OwnerX,OwnerY = GetV(V_POSITION,MyID) -- 主人の座標保持 MyType = GetV(V_HOMUNTYPE,MyID) -- ホムンクルスのタイプを記録 MyPosX = 0 MyPosY = 0 SaveDir = "./AI/" -- ディレクトリ指定(使わない方をコメントアウト:両方コメントアウトするとエラーになります) --SaveDir = "./AI/USER_AI/" -- 旋回動作に関する変数 --------------------------------------------------------------------------------------------------------- RondoFlag = true -- ●旋回動作を行うか否か(trueかfalse で設定 PatrolRest = 90 -- ●ホムの残りHP・SPがこの%以下の場合は静止(回復のため) PatrolInterval = 15000 -- ●一回転してから次に回転するまでの間隔 RondoPattern = 1 -- ●旋回動作のパターン 1:その場で小旋回 2:主人の回りを小旋回 RondoRadius = 1 -- ●旋回動作の半径 RondEnd = os.time() -- 旋回間隔の時間管理 MoveDelay = 0 -- くるくる速度のための設定(初期値0のままでok) Rdx , Rdy = 0,0 -- くるくる目標座標 PatrolVector = 0 -- 現在のくるくる方向(初期値0のままでok) VectorEnd = 0 -- 移動回数設定 NowRondo = false -- 回転動作中 -- 等速追従に関する変数 --------------------------------------------------------------------------------------------------------- DifferentialAccuracy = 140 -- ●移動パケットを送る間隔(短いほど高負荷 長いほど低負荷だが追従が遅れる ChaseDistance = 2 -- ●主人を追跡する時にとる距離(1か2で設定.それ以外では不具合が起きます FollowDistance = 3 -- ●指定セルより離れたら追従を開始する ChaceTime = 0 -- 追跡時間管理 -- オートスキルに関する変数 --------------------------------------------------------------------------------------------------------- AutoAttackSkill = true -- ●自動攻撃スキルを使用するか否か(trueかfalse で設定 AutoBufferSkill = true -- ●自動強化スキルを使用するか否か(trueかfalse で設定 AutoRecoverSkill = false -- ●自動回復スキルを使用するか否か(trueかfalse で設定 AutoSkill = 0 -- ●ホムの自動スキル最低発動確率%(+残りSPにより変動) AutoSkillSPLimit = 20 -- ●ホムのSPがこの%を下回った場合には自動スキル発動なし SetSkillLevel = 5 -- ●使用するムーンライト,カプリスのスキルLv(1〜5で設定 AutoAdjust = true -- ●自動攻撃スキルのレベルを自動調節するか否か(trueかfalse で設定 AS_threshold = {1500,2000,3000,4000,5000} -- ●自動レベル調節の閾値.ミリ秒指定(戦闘時間が〜閾値1:未使用,閾値1〜2:Lv1,… BufferLevel = 1 -- ●オートフリットムーブの使用レベル(1〜5で設定 BufferLimit = 0 -- ●敵の数が指定数以上の時オートフリットムーブ発動 RecoverLevel = 3 -- ●オートリカバーの使用レベル(1〜5で設定 AutoRecoverHP = 50 -- ●オートリカバーが発動するHPの割合(%) EmergencyAvoid = false -- ●移動時に自動で緊急回避を使用するか否か(trueかfalse で設定 EA_Level = 1 -- ●自動緊急回避の使用レベル(1〜5で設定 BloodLust = false -- ●自動強化スキルでアミストル種がブラッドラストを使うか否か(falseならディフェンスを使う MentalChangeAlert = 10 -- ●メンタルチェンジの終了時間が指定秒前になると旋回動作でアラートする(0ならアラートしない StopAS = true -- ●平均戦闘時間により戦闘の途中でASを止めるか否か ASCutTimeRate = 90 -- ●平均戦闘時間の指定%以上の交戦時間ではASしない AttackSkillDelay = 0 -- オートムーンライト用 SkillDelay = 2500 -- スキルディレイ管理 AS_SetFlag = true -- スキルセット用フラグ BufferDelay = 0 -- オートフリットムーブ用ディレイタイム BufferTime = 0 -- 自動強化スキル時間管理 BufferDelay2 = 0 -- オートオーバードスピード用ディレイタイム BufferTime2 = 0 -- 自動強化スキル時間管理 BufferLevel2 = 1 -- オートオーバードスピードの使用レベル(設定により変動 RecoverDelay = 0 -- オートリカバー用ディレイタイム AvoidDelay = 0 -- オート緊急回避用ディレイタイム A_Skill = 0 -- オートムーンライトを設定する変数 A_SkillLevel = 0 -- オートスキルレベル設定 MentalChange = false -- メンタルチェンジ中 SkillTimeOut = true -- ASの時間制限管理 -- スキル使用に関する変数 --------------------------------------------------------------------------------------------------------- LongRangeSkill = true -- ●手動スキルを遠距離から放つか否か(trueかfalse で設定 FirstAttack = false -- ●初撃スキル攻撃をするか否か(trueかfalse で設定 FA_SkillLevel = 1 -- ●初撃スキルの使用レベル(1〜5で設定 FA_OnChase = false -- ●追跡の段階で遠距離から初撃スキルをするか否か LimitSBR = false -- ●S.B.Rを撃つ相手を制限するか否か(trueかfalse で設定 SBR_Target = {} -- ●S.B.Rを撃つ相手の種族IDを指定 CrossFire = false -- ●主人のスキルモーション検知時にオートスキルを100%使用 FirstAttackFlag = true -- 初撃管理フラグ(機能ON/OFFのフラグはこちらではないので注意) OnlySkill = false -- スキルオンリー管理 -- AS確率操作に関する変数 --------------------------------------------------------------------------------------------------------- ASP_Increase = 10 -- ●↑の時,上昇させる%値 ASL_Increase = 1 -- ●↑の時,上昇させるスキルLv AdditionalLevel = 0 -- レベル増加値管理 -- 退避行動に関する変数 --------------------------------------------------------------------------------------------------------- EscapePattern = 0 -- ●ホム危険時,敵から退避するパターン EscapeRange = 3 -- ●ホム危険時,主人の何マス後ろへ隠れるか EscapeDirection = 0 -- ●後ろへ隠れる設定の場合,後ろ以外の方向に変更 -- アクティブ状態に関する変数 --------------------------------------------------------------------------------------------------------- TargetDistance = 7 -- ●タゲに選択する敵のケミからの距離の制限 3〜14程度なら動くはず ActiveFlag = false -- ●アクティブデフォルト設定 falseがノンアクティブ trueがアクティブ ActiveRange = true -- ●索敵範囲を他人との距離に応じて変化させるか否か LimitRangeDist = 6 -- ●この距離以内に人が居る場合は,アクティブ索敵しない HalfActive = true -- ●任意のMobはアクティブに攻撃しにいく ActiveHP = 40 -- ●指定%以下のHPの時はノンアクティブ ActiveSP = 0 -- ●指定%以下のSPの時はノンアクティブ OthersRange = 4 -- ●ターゲットの指定セル以内に他プレイヤーが居る場合はターゲットリセット OnActive = false -- アクティブ索敵中フラグ -- 通常攻撃に関する変数 --------------------------------------------------------------------------------------------------------- FollowOwnerTargetting = false -- ●主人のタゲを最優先にするかどうか (する→true  しない→false GuardOwner = false -- ●主人が攻撃されていたら最優先でターゲット OnBattleDistance = 7 -- ●戦闘中、この距離以上主人が離れると戦闘を中断して追従 OnBattleDistancePlus = 10 -- ●友達が居る場合、OnBattleDistanceを指定数のセルまで拡張する.拡張不要であれば上記と同値に. OBD_Neutral = OnBattleDistance -- 初期値を記憶 AttackDistance = 1 -- ●攻撃する時の敵との距離.(1か2以外では不具合の恐れ MultiAttack = 2 -- ●1サイクルの攻撃回数(攻撃モーションキャンセル:値域1〜3,数字の数だけ攻撃コマンド OverlapFlag = 0 -- 戦闘中、敵と重なったときに移動するためのフラグ変数 GuardOwnerFlag = false -- 主人の敵ターゲットフラグ AttackedFlag = false -- 攻撃管理 AttackStCycle = 0 -- 攻撃状態回数管理 -- 座標重なり回避に関する変数 --------------------------------------------------------------------------------------------------------- HateOverlap = true -- ●セル重なり回避をするか否か(trueかfalse で設定 AwayType = 1 -- ●回避タイプ(0:絶対方向(AvoidDirectionで指定),1:相対方向(常に敵の背後へ)) AwayStep = 2 -- ●セル重なり回避のときに移動するセル数 AvoidDirection = {{x=1,y=0},{x=0,y=-1},{x=-1,y=0},{x=0,y=1}} -- ●重なり回避の退避方向(-1〜1でx,y方向指定) AwayCount = TableSize(AvoidDirection) -- 重なり回避の時の移動試行回数 -- 友達登録に関する変数 --------------------------------------------------------------------------------------------------------- AutoFriend = true -- ●自動友達登録するか否か TamedDegree = 20 -- ●自動友達登録の懐きやすさ.20〜40前後がよいと思われる.低いとすぐ登録するが,誤登録の可能性も. Neighbor = 0 -- 友達の可能性がある人のID NeighborFlag = false -- 管理フラグ NeighborTime = 0 -- 時間管理 FriendList = {} -- 友達リスト FriendsEnemy = 0 -- 友達のターゲット FriendExists = false -- 近くに友達が居るか SameTargetCount = 0 -- タゲ被り回数 HomFriendList = {} -- ホムンクルスの友達リスト(ホムIDはロードごとリセット -- 射撃状態に関する変数 --------------------------------------------------------------------------------------------------------- ShootingMode = false -- ●戦闘は常に射撃状態で行うか否か ShootLevel = 3 -- ●射撃状態時に使用するスキルのスキルLv PatrolPattern = 6 -- ●射撃状態時に接敵された場合,退避する旋回パターン(待機時旋回と同様) PatrolRange = 3 -- ●射撃状態時に接敵された場合,退避する距離(主人からの距離) -- 寝落ち防止機能に関する変数 ---------------------------------------------------------------------------- AutoExit = false -- ●寝落ち防止を利用するか否か ExitLimitTime = 1800 -- ●寝落ち防止を何秒で実行するか(デフォは1800秒=30分 ExitCheckTime = 0 -- 寝落ちチェック間隔管理 -- 共闘モード関する変数 ---------------------------------------------------------------------------- BonusMode = false -- ●共闘モードにするかどうか BonusTime = 500 -- ●指定ミリ秒間だけ攻撃する BonusBorder = 3 -- ●こちらをターゲットしている敵の数が指定数以上の場合は共闘モードを一時解除 BonusFlag = false -- 共闘モード管理フラグ -- 詠唱妨害関する変数 ---------------------------------------------------------------------------- PreventEnemyCasting = true -- ●詠唱妨害するかどうか PreventSkillLevel = 1 -- ●詠唱妨害スキルのレベル -- SP回復待ちに関する変数 ---------------------------------------------------------------------------- WaitSPR = false -- ●4秒に1回のSPRを待ち,タイミング次第で移動を制限する SPR_Wait = 3 -- ●指定秒数以上移動しない時間が続いたら,4秒まで待つ SPR_Rate = 80 -- ●SP量が指定%以下のときにSPR待ちをする SPR_Flag = false -- SPR管理フラグ StayTime = os.time() -- 滞在時間 PreSP = 0 -- 回復前SP -- 先行移動に関する変数 ---------------------------------------------------------------------------- OnForward = false -- ●移動時にホムンクルスが先導するか否か ForwardDistance = 3 -- ●先行する距離 PrevPos1x, PrevPos1y = GetV(V_POSITION,MyOwner) -- 前回居た位置 PrevPos2x, PrevPos2y = GetV(V_POSITION,MyOwner) -- 前々回居た位置 PrevPos3x, PrevPos3y = GetV(V_POSITION,MyOwner) -- 前々々回居た位置 -- フック攻撃に関する変数 ---------------------------------------------------------------------------- HookShot = false -- ●敵をフックするか否か HookTime = 0 -- フック時間管理 HookFlag = 0 -- フックフラグ管理 -- スタックチェックに関する変数 ---------------------------------------------------------------------------- AttackFalseCheck = true -- ●攻撃スタックをチェックする StandTime = 0 -- 停止時間 AttackFalseCount = 0 -- 攻撃ミス回数 AttackFalseTime = GetTick() -- 攻撃ミス時間 SkipStack = false -- サーチングに関する変数 ---------------------------------------------------------------------------- TargetObjects = { 111 , 113 } -- ターゲットフラグ SearchedObject = 0 -- 探索対象ID -- 索敵優先度に関する変数 --------------------------------------------------------------------------------------------------- -- 詳細はマニュアルを参照のこと EnemyPriority = {3, 2, 1, 5, 4} -- ●敵選択優先度の設定.配列内の数字の順位で設定.条件を4つ以下に設定しても有効 RankedEnemySelect = 1 -- ●フリーの敵選択時,優先度設定(0:一番近い敵,1:一番弱い敵,2:一番強い敵 -- メッセージ出力に関する変数 ---------------------------------------------------------------------------- PrintMsg = false -- ●メッセージを専用ウィンドウに出力するか否か PrintTalk = false -- ●メッセージウィンドウにトークメッセージを出力するか否か msgList = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- メッセージリスト PrintableFlag = true -- プリントフラグ PrintTimeOut = os.time() -- 時間管理 MsgHoldTime = 0 -- 表示時間管理 EDIT_ST = 100 TRIGGER_ST = 101 MODE_ST = 102 -- その他の変数 --------------------------------------------------------------------------------------------------------- MoveToStay = false -- ●移動命令後,休息状態にする SleepFlag = true -- ●スリープフラグ(falseにするとスリープしない SleepTime = 1000 -- ●スリープ時間(ミリ秒) MC_AlertPattern = 9 -- ●MChアラートのパターン MovingCancel = false -- ●移動によるモーションキャンセル MovingSP = 30 -- ●モーションキャンセルはSPがこれ以上のとき限定 MC_AlertFlag = false -- MChアラート用フラグ StayFlag = false -- 移動命令時の留まりを管理 AllEnemys = {} -- 敵リスト AllEnemysCount = 0 -- 敵の数 LostEnemy = 0 -- さっき倒した敵 LastState = 0 -- 直前の状態 StepCommand = {0,0,0} -- ステップコマンド格納 SCIndex = 0 -- ステップコマンド用インデックス T_TriggerTime = 0 -- Alt+Tタイミング管理 ReturnFlag = false -- リターンフラグ ManualAttack = false -- 手動攻撃フラグ LagCheckFlag = false -- ラグチェック -- 平均戦闘時間に関する変数 ---------------------------------------------------------------------------- MobFilename = "Mob" RecordBattleTime = true -- ●敵ごとに平均戦闘時間を記録する RecCount = 10 -- ●戦闘時間の平均を取るサンプル数 KillCount = {} -- 内部キルカウント DistTime = {} -- 撃破時間 OnBattleTime = 0 -- 戦闘時間 -- 登録操作系編集に関する変数 ---------------------------------------------------------------------------- -- デフォルトの操作系をある程度カスタマイズできます.組み合わせによっては作動しないこともあります ALT_SHIFT_CELL_CLICK = EDIT_FRIEND -- Alt+Shift+地面右クリック ALT_SHIFT_OWNER_CLICK = EDIT_FRIEND_ALL -- Alt+Shift+主人の居るセル右クリック ALT_SHIFT_MY_CLICK = EDIT_FRIEND_DEL -- Alt+Shift+自分の居るセル右クリック ALT_D_CLICK = EDIT_RANK -- ●Alt+対象ダブル右クリック ALT_D_CLICK_SIT = EDIT_BONUS -- ●主人が座ってる時,Alt+対象ダブル右クリック ALT_SHIFT_CLICK = EDIT_RAID -- ●Alt+Shift+対象右クリック ALT_SHIFT_CLICK_SIT = EDIT_FRIEND -- ●主人が座ってる時,Alt+Shift+対象右クリック ALT_T_SHIFT_CLICK = EDIT_IGNORE -- ●休息状態時,Alt+Shift+対象右クリック ATTACKSKILL_LV1 = EDIT_SKILL -- ●攻撃スキルLv1を使用 ATTACKSKILL_LV2 = EDIT_CAST -- ●攻撃スキルLv2を使用 ALT_T_ATTACKSKILL = EDIT_SKILL_LEVEL -- ●休息状態時,攻撃スキルを使用 -- トリガー系編集に関する変数 ---------------------------------------------------------------------------- -- 特定の条件に任意の機能やパラメータ変動を割り当てることが出来ます. MANY_ENEMYS = {TRIGGER_ASP_INC,TRIGGER_ASL_INC} -- ●敵の数がEnemyBorder_N以上の時の行動 TOUGH_ENEMY = {TRIGGER_ASP_INC} -- ●平均戦闘時間記録がEnemyBorder_T以上の時の行動 ALT_T_TO_REST = {} -- ●Alt+Tで待機→休息状態に移行する時の行動 ALT_T_TO_IDLE = {} -- ●Alt+Tで休息→待機状態に移行する時の行動 ALT_MY_CLICK = {TRIGGER_OnSEARCH} -- ●自分の居るセルをAlt+右クリックしたときの行動 ALT_OWNER_CLICK = {} -- ●主人の居るセルをAlt+右クリックしたときの行動 ALT_FRIEND_CLICK = {TRIGGER_CHANGE_OWNER} -- ●友達の居るセルをAlt+右クリックしたときの行動 MY_HP_UNDER_SAFETY = {TRIGGER_OnESCAPE} -- ●自分のHPがHomunculusSafetyHPで指定した%以下になった時の行動 MY_SP_UNDER_SAFETY = {} -- ●自分のSPがHomunculusSafetyHPで指定した%以下になった時の行動 OWNER_HP_UNDER_SAFETY = {} -- ●主人のSPがHomunculusSafetyHPで指定した%以下になった時の行動 OWNER_SP_UNDER_SAFETY = {} -- ●主人のSPがHomunculusSafetyHPで指定した%以下になった時の行動 -- トリガーに関係する設定変数 HomunculusSafetyHP = 30 -- ●自分のHPが指定%以下のとき,任意の行動をとる HomunculusSafetySP = 60 -- ●自分のSPが指定%以下のとき,任意の行動をとる OwnerSafetyHP = 30 -- ●主人のHPが指定%以下のとき,任意の行動をとる OwnerSafetySP = 20 -- ●主人のSPが指定%以下のとき,任意の行動をとる EnemyBorder_N = 3 -- ●敵の数が指定数以上のとき,任意の行動をとる EnemyBorder_T = 7000 -- ●ターゲットの平均戦闘時間が指定ミリ秒以上の時,任意の行動を取る T_MoveDirection = {-3,3} -- ●トリガー発行時,現在地から指定の相対座標へ移動(デフォルト:北3西3セルへ) -- モードチェンジに関する変数 ---------------------------------------------------------------------------- QuickTrigger = false -- ●Alt+T連打によるスイッチを使うか否か ModeList = {3,4,1} -- ●モードリスト(リーフ:1 アミストル:2 フィーリル:3 バニルミルト:4) ModeIndex = 1 -- モードインデックス -- 友達リストファイル読み込み --------------------------------------------------------------------------------------------------------- if io.open(SaveDir.."Friend.lua") then local friendFile = SaveDir.."Friend.lua" require(friendFile) --TraceAI(" ! ReadFile : "..friendFile) end FriendIndex = TableSize(FriendList) -- 友達リストの番号 -- 外部設定ファイル読み込み --------------------------------------------------------------------------------------------------------- if io.open(SaveDir.."Set.lua") then local setFile = SaveDir.."Set.lua" require (setFile) Setting() --TraceAI(" ! ReadFile : "..setFile) end -- 設定ファイル読み込み後の再定義 ---------------------------------------------------------------------------------------------------- ASP_Neutral = AutoSkill -- 確率初期値 OBD_Neutral = OnBattleDistance -- 初期値を記憶 AD_Neutral = AttackDistance if io.open(SaveDir.."CustomCommand.lua") then local cmdFile = SaveDir.."CustomCommand.lua" require (cmdFile) --TraceAI(" ! ReadFile : "..cmdFile) CustomCommand = true end -- Mob別設定配列の作成 ---------------------------------------------------------------------------------------------------- Mob = {} -- 配列設定 InitializedTime = GetTick() InitialFlag = false --TraceAI("----- Initialized ----- ") end -- 設定変数ここまで ----------------------------------------------------------------------------------------------- ------------------------------------------------- -- GetV用定数 ------------------------------------------------- -------------------------------- V_OWNER = 0 -- 召喚者の ID V_POSITION = 1 -- 座標 (x、y) V_TYPE = 2 -- タイプ (未実装) V_MOTION = 3 -- 現在の命令 V_ATTACKRANGE = 4 -- 物理攻撃範囲 (未実装。現在は1セルに固定) V_TARGET = 5 -- 攻撃、スキル使用対象の ID V_SKILLATTACKRANGE = 6 -- スキル攻撃範囲 (未実装) V_HOMUNTYPE = 7 -- ホムンクルスの種類 V_HP = 8 -- HP (ホムンクルスと召喚者) V_SP = 9 -- SP (ホムンクルスと召喚者) V_MAXHP = 10 -- 最大 HP (ホムンクルスと召喚者) V_MAXSP = 11 -- 最大 SP (ホムンクルスと召喚者) --------------------------------- -------------------------------------------- -- ホムンクルスの種類 -------------------------------------------- LIF = 1 AMISTR = 2 FILIR = 3 VANILMIRTH = 4 LIF2 = 5 AMISTR2 = 6 FILIR2 = 7 VANILMIRTH2 = 8 LIF_H = 9 AMISTR_H = 10 FILIR_H = 11 VANILMIRTH_H = 12 LIF_H2 = 13 AMISTR_H2 = 14 FILIR_H2 = 15 VANILMIRTH_H2 = 16 -------------------------------------------- -------------------------- -- コマンド定数 -------------------------- NONE_CMD = 0 MOVE_CMD = 1 STOP_CMD = 2 ATTACK_OBJECT_CMD = 3 ATTACK_AREA_CMD = 4 PATROL_CMD = 5 HOLD_CMD = 6 SKILL_OBJECT_CMD = 7 SKILL_AREA_CMD = 8 FOLLOW_CMD = 9 -------------------------- ----------------------------- -- 状態定数 ----------------------------- IDLE_ST = 0 FOLLOW_ST = 1 CHASE_ST = 2 ATTACK_ST = 3 MOVE_CMD_ST = 4 STOP_CMD_ST = 5 ATTACK_OBJECT_CMD_ST = 6 ATTACK_AREA_CMD_ST = 7 PATROL_CMD_ST = 8 HOLD_CMD_ST = 9 SKILL_OBJECT_CMD_ST = 10 SKILL_AREA_CMD_ST = 11 FOLLOW_CMD_ST = 12 -- 追加状態定数 HOOK_ST = 24 -- フック中状態定数 ESCAPE_ST = 25 -- 逃避行動状態の状態定数 SHOOTING_ST = 26 -- 状態変数 ALERT_ST = 27 -- アラートパフォーマンス用 FORWARD_ST = 28 -- 先導状態定数 SEARCH_ST = 29 -- 探索状態定数 ---------------------------- -------------------------- -- motion -------------------------- MOTION_STAND = 0 -- 立っているモーション(動作) MOTION_MOVE = 1 -- 移動中のモーション MOTION_ATTACK = 2 -- 攻撃中のモーション MOTION_DEAD = 3 -- 死んで倒れるモーション MOTION_DAMAGE = 4 -- ダメージを受けた時のモーション MOTION_PICKUP = 5 -- 拾うモーション MOTION_SIT = 6 -- 座るモーション MOTION_SKILL = 7 -- スキル攻撃中モーション(インベ、カートレボリューション、ラウドボイス確認) MOTION_CASTING = 8 -- 詠唱モーション MOTION_ATTACK2 = 9 -- 攻撃モーション MOTION_PP = 12 -- ポーションピッチャーのモーション MOTION_SLIMPP = 28 -- スリムポーションピッチャー&ADSのモーション -------------------------- ------------------------------------------ -- グローバル変数(オリジナル) ------------------------------------------ MyState = IDLE_ST -- 最初の状態は待機 MyEnemy = 0 -- 敵 id MyDestX = 0 -- 目的地 x座標 MyDestY = 0 -- 目的地 y座標 MyID = 0 -- ホムンクルス id MySkill = 0 -- ホムンクルスのスキル MySkillLevel = 0 -- ホムンクルスのスキルレベル ------------------------------------------ -- MobRec用グローバル定数 ------------------------------------------------------------------------------------------------ -- Mob[ID][value] value:ACT, SKILL, CAST, BOOST, RANK, RECORD M_ACT = 1 -- Mobに対する振る舞い ACT_ESCAPE = 0 -- ├逃げる ACT_NO = 1 -- ├一切何もしない ACT_IGNORE = 2 -- ├無視する(主人等のターゲットは攻撃) ACT_NORMAL = 3 -- ├通常 ACT_RAID = 4 -- └先制攻撃 M_SKILL = 2 -- スキルに関して SKILL_NO = 0 -- ├攻撃系スキルを使わない SKILL_AUTO = 6 -- ├スキルを使う(デフォルト設定) SKILL_FULL_AUTO = 12 -- └フルフラット(オートレベル) M_AS = 3 -- オートスキルの個別基本確率設定 M_CAST = 4 -- 詠唱妨害 CAST_OFF = 0 -- ├しない CAST_ON = 1 -- └する M_BOOST = 5 -- 特殊強化系スキルに関して(MCh,OvS) BOOST_OFF = 0 -- ├使わない(デフォルト設定) M_RANK = 6 -- 個別索敵優先度 M_RECORD = 7 -- 平均戦闘時間用 -- EditControl用グローバル定数 ------------------------------------------------------------------------------------------------ -- 割り当て用操作ごとに定数 EDIT_NONE = 0 -- なにもしない EDIT_FRIEND = 1 -- 友達登録操作 EDIT_SKILL = 2 -- スキル使用可否操作 EDIT_SKILL_LEVEL = 3 -- スキルレベル登録操作 EDIT_IGNORE = 4 -- 無視設定登録操作 EDIT_RAID = 5 -- 先制攻撃登録操作 EDIT_BOOST = 6 -- 特殊強化対象登録操作 EDIT_CAST = 7 -- 詠唱妨害対象登録操作 EDIT_RANK = 8 -- 優先度増加操作 EDIT_BONUS = 9 -- 手加減対象登録操作 EDIT_FULL_SKILL = 10 -- フルスキル対象登録操作 EDIT_FRIEND_ALL = 11 -- 画面内全員友達登録操作 EDIT_FRIEND_DEL = 12 -- 友達リスト全削除操作 -- TriggerControl用グローバル定数 ------------------------------------------------------------------------------------------------ T_FLAG = {} -- フラグ管理 TRIGGER_ON = 100 -- トリガーON TRIGGER_OFF = 101 -- トリガーOFF TRIGGER_PING = 102 -- 単発条件 TRIGGER_OnESCAPE = 1 -- 退避状態化 TRIGGER_OnSEARCH = 2 -- サーチング TRIGGER_OnFORWARD = 3 -- 先行移動化 TRIGGER_OnSHOOTING = 4 -- 援護射撃化 TRIGGER_ACTIVE = 5 -- アクティブ化 TRIGGER_CHANGE_OWNER = 6 -- オーナーチェンジ TRIGGER_ASP_INC = 7 -- AS時の確率増加 TRIGGER_ASL_INC = 8 -- AS時のレベル増加 TRIGGER_OnMOVE = 9 -- 指定座標へ移動 TRIGGER_CHANGE_MODE = 10 -- モードチェンジ SkillObjectL = SkillObject -- Util.luaと同等の内容-------------------------------------------------------------------------------------------- function GetDistance (x1,y1,x2,y2) return math.floor(math.sqrt((x1-x2)^2+(y1-y2)^2)) end function GetDistance2 (id1, id2) local x1, y1 = GetV (V_POSITION,id1) local x2, y2 = GetV (V_POSITION,id2) if (x1 == -1 or x2 == -1) then return -1 end return GetDistance (x1,y1,x2,y2) end function IsOutOfSight (id1,id2) local x1,y1 = GetV (V_POSITION,id1) local x2,y2 = GetV (V_POSITION,id2) if (x1 == -1 or x2 == -1) then return true end local d = GetDistance (x1,y1,x2,y2) if d > 20 then return true else return false end end ---------------------------------------------------------- -- リストをセーブする ---------------------------------------------------------- function SaveFriend(file,list) local filename = SaveDir..file..".lua" local fp = io.open(filename,"w") if (fp ~= nil) then -- ファイルが既にあれば for i,v in pairs (list) do fp:write(string.format("FriendList[%d]=%d\n",i, v)) -- 一行ずつ書き出し end fp:close() end --TraceAI(" ! Save File : "..filename) end ---------------------------------------------------------- -- テーブルのサイズを返す ---------------------------------------------------------- function TableSize(list) local num = 0 for i,v in pairs (list) do num = num + 1 end return num end ---------------------------------------------------------- -- 絶対値 ---------------------------------------------------------- function MathABS(n) if n < 0 then return -1*n else return n end end ---------------------------------------------------------- -- 剰余 n%t ---------------------------------------------------------- function MathMod(n,t) if not n or not t then return 0 end return (n - math.floor(n/t)*t) end ---------------------------------------------------------- -- 閾値で数を区切る n-t ---------------------------------------------------------- function MathThreshold(n,t) if not n or not t then return 0 end if n <= t then return n else return n - t end end ------------------------------------------------------- -- 旋回ダンスに関する定義 ------------------------------------------------------- function Rondo(pattern,radius) local mx, my = GetV(V_POSITION,MyID) if PatrolVector == 0 then SetRondo(pattern,radius) Rdx,Rdy = mx,my PatrolVector = 1 end if (Rdx == mx and Rdy == my) or MoveDelay < GetTick() then -- 目標地点に到達したか,移動ディレイが経過した if PatrolVector <= VectorEnd then NowRondo = true Rdx,Rdy = rondDist[PatrolVector].x,rondDist[PatrolVector].y Move (MyID,rondDist[PatrolVector].x,rondDist[PatrolVector].y) MoveDelay = MoveSpeed*GetDistance(mx,my,rondDist[PatrolVector].x,rondDist[PatrolVector].y) -- 移動時間計算 MoveDelay = GetTick() + MoveDelay else PatrolVector = 0 NowRondo = false RondEnd = os.time() if PatrolInterval == 0 then Rondo(RondoPattern,RondoRadius) end return end PatrolVector = PatrolVector +1 end end function SetRondo(pattern,radius) NowX, NowY = GetV (V_POSITION,MyID) -- 座標取得 MoveSpeed = 150 -- くるくる速度係数 rondDist = {} local sub = math.floor(radius*2/3) if pattern == 1 then -- その場で旋回 rondDist = {{x=NowX-radius,y=NowY+radius},{x=NowX,y=NowY+radius*2},{x=NowX+radius,y=NowY+radius},{x=NowX,y=NowY}} VectorEnd = 4 elseif pattern == 2 then -- 主人の周りを旋回(菱形) rondDist = {{x=OwnerX-radius,y=OwnerY},{x=OwnerX,y=OwnerY+radius},{x=OwnerX+radius,y=OwnerY},{x=OwnerX,y=OwnerY-radius}} VectorEnd = 4 elseif pattern == 3 then -- 主人の周りを旋回(正方形) rondDist = {{x=OwnerX-radius,y=OwnerY-radius},{x=OwnerX-radius,y=OwnerY+radius},{x=OwnerX+radius,y=OwnerY+radius},{x=OwnerX+radius,y=OwnerY-radius}} VectorEnd = 4 elseif pattern == 4 then -- 主人の座標を交点に十字 rondDist = {{x=OwnerX-radius,y=OwnerY},{x=OwnerX+radius,y=OwnerY},{x=OwnerX+1,y=OwnerY},{x=OwnerX,y=OwnerY+1},{x=OwnerX,y=OwnerY+radius},{x=OwnerX,y=OwnerY-radius},{x=OwnerX,y=OwnerY-1}} VectorEnd = 7 elseif pattern == 5 then -- 主人の周りを旋回(円形) rondDist = {{x=OwnerX,y=OwnerY-radius},{x=OwnerX-sub,y=OwnerY-radius},{x=OwnerX-radius,y=OwnerY-sub},{x=OwnerX-radius,y=OwnerY+sub},{x=OwnerX-sub,y=OwnerY+radius},{x=OwnerX+sub,y=OwnerY+radius},{x=OwnerX+radius,y=OwnerY+sub},{x=OwnerX+radius,y=OwnerY-sub},{x=OwnerX+sub,y=OwnerY-radius},{x=OwnerX,y=OwnerY-radius}} VectorEnd = 10 elseif pattern == 6 then -- 主人の座標を中心に8の字を描く rondDist = {{x=OwnerX-radius,y=OwnerY+sub},{x=OwnerX,y=OwnerY+radius},{x=OwnerX+radius,y=OwnerY+sub},{x=OwnerX+1,y=OwnerY},{x=OwnerX-radius,y=OwnerY-sub},{x=OwnerX,y=OwnerY-radius},{x=OwnerX+radius,y=OwnerY-sub},{x=OwnerX-1,y=OwnerY}} VectorEnd = 8 elseif pattern == 8 then -- 範囲内をランダム local i = 1 VectorEnd = 10 while i <= VectorEnd do rondDist[i] = {x=OwnerX-(radius+1)+math.random(1+radius*2),y=OwnerY-(radius+1)+math.random(1+radius*2)} i = i + 1 end elseif pattern == 9 then -- アラート用 MoveSpeed = 170 rondDist = {{x=OwnerX-1,y=OwnerY},{x=OwnerX,y=OwnerY+1},{x=OwnerX+1,y=OwnerY},{x=OwnerX,y=OwnerY-1}} VectorEnd = 4 elseif pattern == 10 then -- サーチング用 MoveSpeed = 170 rondDist = {{x=MyDestX-1,y=MyDestY},{x=MyDestX,y=MyDestY+1},{x=MyDestX+1,y=MyDestY},{x=MyDestX,y=MyDestY-1}} VectorEnd = 4 end end ------------------------------------------------------- -- MoveToNearについて定義 -- IDからDist離れた位置へ移動.Distは1か2推奨 ------------------------------------------------------- function MoveToNear (id,dist) local x,y = GetV(V_POSITION,id) local mx,my = GetV(V_POSITION,MyID) if not SPR_Flag or MyState == CHASE_ST or MyState == HOOK_ST then if dist < 3 then if GetDistance2(MyID,id) > 5 then Move(MyID,x,y) else local dx = dist local dy = dist if MyState == FOLLOW_ST or MyState == FOLLOW_CMD_ST then local MdirX,MdirY = VectorCheck(mx,my,x,y) -- 自分から主人への方向 local OdirX,OdirY = VectorCheck(PrevPos1x,PrevPos1y,x,y) -- 主人の方向 if OdirX ~= 0 or OdirY ~= 0 then if MdirX ~= OdirX then dx = MathABS(dx-1) end if MdirY ~= OdirY then dy = MathABS(dy-1) end end --TraceAI("ox oy = "..OdirX.." "..OdirY.." dx dy = "..MdirX.." "..MdirY.." +x +y = "..dx.." "..dy) end if MathABS(x - mx) > dist then if x < mx then x = x + dx else x = x - dx end elseif MyState ~= FOLLOW_ST and MyState ~= FOLLOW_CMD_ST then x = mx end if MathABS(y - my) > dist then if y < my then y = y + dy else y = y - dy end elseif MyState ~= FOLLOW_ST and MyState ~= FOLLOW_CMD_ST then y = my end Move(MyID,x,y) end else if GetDistance2(MyID,id) > 4 then x = math.floor((x+mx)/2) y = math.floor((y+my)/2) Move (MyID,x,y) end end end end ------------------------------------------------------- -- 移動先の位置をテンキーの数値で表現し,返す関数 ------------------------------------------------------- function InputCommand(x,y) OwnerX, OwnerY = GetV(V_POSITION,GetV(V_OWNER,MyID)) if x+1 == OwnerX and y == OwnerY then -- ←:主人の左(4) return 4 elseif x-1 == OwnerX and y == OwnerY then -- →:主人の右(6) return 6 elseif x == OwnerX and y+1 == OwnerY then -- ↓:主人の下(2) return 2 elseif x == OwnerX and y-1 == OwnerY then -- ↑:主人の上(8) return 8 else return 0 end end -------------------------------------------------------****------------------------------------------------------- -- ステップコマンドを判定,実行する関数 -------------------------------------------------------****------------------------------------------------------- function JudgeCommand() -- コマンド判定開始 if CustomCommand then -- カスタムファイルがあるときは JudgeCommand2() -- カスタムファイルのコマンドを実行 elseif StepCommand[1] == 4 and StepCommand[2] == 6 and StepCommand[3] == 4 then -- ←→← で ばくれつけんだ! SkillObject (MyID,5,8010,MyID) --TraceAI ("--> Command Succses!! Full Boost") ASP_Neutral_D = ASP_Neutral ASP_Neutral = 100 elseif StepCommand[1] == 6 and StepCommand[2] == 2 and StepCommand[3] == 6 then -- →↓→で援護射撃モード MyState = SHOOTING_ST --TraceAI ("--> Command Succses!! Shooting Mode") elseif StepCommand[1] == 2 and StepCommand[2] == 2 and StepCommand[3] == 2 then -- ↓↓↓でばくれつけんリセット --TraceAI ("--> Command Succses!! Rest") if ASP_Neutral_D then ASP_Neutral = ASP_Neutral_D end MyState = ALERT_ST -- 主人の周りを旋回して合図 elseif StepCommand[1] == 4 and StepCommand[2] == 4 and StepCommand[3] == 4 then -- ←←←でSP節約 --TraceAI ("--> Command Succses!! AutoAttackSkill OFF") AutoAttackSkill = false MyState = ALERT_ST -- 主人の周りを旋回して合図 elseif StepCommand[1] == 6 and StepCommand[2] == 6 and StepCommand[3] == 6 then -- →→→でオートスキル使用 --TraceAI ("--> Command Succses!! AutoAttackSkill ON") AutoAttackSkill = true MyState = ALERT_ST -- 主人の周りを旋回して合図 elseif StepCommand[1] == 8 and StepCommand[2] == 2 and StepCommand[3] == 6 then -- ↑↓→でマスター委譲 --TraceAI ("--> Command Succses!! Change Owner") ChangeOwner() -- 偽主人の周りを旋回 elseif StepCommand[1] == 4 and StepCommand[2] == 6 and StepCommand[3] == 2 then -- ←→↓で共闘モードスイッチ if BonusMode then --TraceAI ("--> Command Succses!! BonusMode OFF") BonusMode = false else --TraceAI ("--> Command Succses!! BonusMode ON") BonusMode = true end MyState = ALERT_ST elseif StepCommand[1] == 8 and StepCommand[2] == 8 and StepCommand[3] == 8 then -- ↑↑↑で全ての設定を初期化 --TraceAI ("--> Command Succses!! Initialize") InitialFlag = true MyState = ALERT_ST -- 主人の周りを旋回して合図 elseif StepCommand[1] == 2 and StepCommand[2] == 4 and StepCommand[3] == 2 then -- ↓←↓で友達リストクリア ClearFriendList() end -- ここでコマンド判定終了 end ------------------------------------------------------- -- ステップコマンドをクリアする関数 ------------------------------------------------------- function CommandClear() StepCommand = {0, 0, 0} SCIndex = 0 end ------------------------------------------------------- -- 移動コマンドを受けた時に関する定義 ------------------------------------------------------- function OnMOVE_CMD (x,y) TraceAI ("OnMOVE_CMD") -- ステップコマンド入力処理 if GetDistance2(MyID,GetV(V_OWNER,MyID)) <= 2 then local command = InputCommand(x,y) SCIndex = SCIndex + 1 StepCommand[SCIndex] = command --TraceAI (" -> Command"..SCIndex.." : "..StepCommand[SCIndex]) if command == 0 then CommandClear() end if StepCommand[3] ~= 0 then JudgeCommand() CommandClear() return end else CommandClear() end local id = GetID(x,y) local mx, my = GetV(V_POSITION,MyID) if x == mx and y == my then TriggerControl(TRIGGER_PING,ALT_MY_CLICK) -- トリガー:自分のセルALT+右クリック return elseif id == GetV(V_OWNER,MyID) then TriggerControl(TRIGGER_PING,ALT_OWNER_CLICK) -- トリガー:主人のセルALT+右クリック return elseif IsFriend(id) == 1 then TriggerControl(TRIGGER_PING,ALT_FRIEND_CLICK,id) -- トリガー:友達のセルALT+右クリック return end local curX, curY = GetV (V_POSITION,MyID) if (MathABS(x-curX)+MathABS(y-curY) > 15) then -- 目的地が一定距離以上なら (サーバーで遠距離は処理しないため) return end if MyState ~= MOVE_CMD_ST then LastState = MyState end MyState = MOVE_CMD_ST MyDestX = x MyDestY = y end ------------------------------------------------------- -- 通常攻撃コマンドを受けた時に関する定義 ------------------------------------------------------- function OnATTACK_OBJECT_CMD (id) --TraceAI ("OnATTACK_OBJECT_CMD") if id ~= MyEnemy then ResetEnemy() end MySkill = 0 MyEnemy = id MyState = CHASE_ST ManualAttack = true local o_motion = GetV(V_MOTION,GetV(V_OWNER,MyID)) -- 主人のモーション if o_motion ~= MOTION_SIT then -- 座り以外なら EditControl(ALT_D_CLICK,id) -- 登録操作 else EditControl(ALT_D_CLICK_SIT,id) -- 登録操作 end end ------------------------------------------------------- -- 対象指定スキルコマンドを受けた時に関する定義 ------------------------------------------------------- function OnSKILL_OBJECT_CMD (level,skill,id) local enemy = GetV(V_HOMUNTYPE,id) -- 敵の種別ID if skill == 8012 and LimitSBR then -- スキルがS.B.Rだったら local num = TableSize(SBR_Target) for i,target in ipairs(SBR_Target)do if enemy == target then -- リストにIDがあったら break -- ループ抜ける elseif i == num then -- 見つからずに最後まで来ちゃったら return -- スキルを使わない end end else if MyState ~= FOLLOW_CMD_ST then if level == 1 then EditControl(ATTACKSKILL_LV1,id) elseif level == 2 then EditControl(ATTACKSKILL_LV2,id) end else EditControl(ALT_T_ATTACKSKILL,id,level) end end --TraceAI ("OnSKILL_OBJECT_CMD") MySkillLevel = level MySkill = skill MyEnemy = id if skill == 8009 then -- スキルがムーンライト SkillDelay = 2500 -- ディレイ2.5秒 elseif skill == 8012 then -- SBR44 SkillDelay = 1 elseif skill == 8013 then -- カプリス SkillDelay = 1000+200*level else SkillDelay = 0 end if LongRangeSkill then SkillObject(MyID,MySkillLevel,MySkill,MyEnemy) MySkill = 0 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定(2.5秒くらい --TraceAI ("AttackSkillDelay ----> "..AttackSkillDelay) else MyState = ATTACK_ST end end ------------------------------------------------------- -- 追従コマンドを受けた時に関する定義 ------------------------------------------------------- function OnFOLLOW_CMD () -- 待機命令は、待機状態と休息状態を互いに転換させる if (MyState ~= FOLLOW_CMD_ST) then TriggerControl(TRIGGER_PING,ALT_T_TO_REST) ResetEnemy() MoveToNear (MyOwner,ChaseDistance) MyState = FOLLOW_CMD_ST --MyDestX, MyDestY = GetV (V_POSITION,MyOwner) MySkill = 0 if QuickTrigger then T_TriggerTime = GetTick() end else TriggerControl(TRIGGER_PING,ALT_T_TO_IDLE) -- トリガー操作 if GetV(V_MOTION,MyOwner) == MOTION_SIT then -- 主人が座っている時のみ,アクティブ・ノンアクティブを切り替える if not ActiveFlag then ActiveFlag = true -- アクティブ化 Move(MyID,OwnerX,OwnerY+1) -- アクティブ化したら主人の北に移動で合図 else ActiveFlag = false -- ノンアクティブ化 Move(MyID,OwnerX,OwnerY-1) -- ノンアクティブ化したら主人の南に移動で合図 end else if GetTick() - T_TriggerTime < 500 then -- 0.5秒以内にもう一度Alt+Tが押された場合 ShiftMode(ModeList) StayFlag = false MyState = ALERT_ST return end end MySkill = 0 StayFlag = false MyState = IDLE_ST end end ------------------------------------------------------- -- コマンドを受け付けたときの処理 ------------------------------------------------------- function ProcessCommand (msg) if (msg[1] == MOVE_CMD) then OnMOVE_CMD (msg[2],msg[3]) --TraceAI ("MOVE_CMD") elseif (msg[1] == ATTACK_OBJECT_CMD) then OnATTACK_OBJECT_CMD (msg[2]) --TraceAI ("ATTACK_OBJECT_CMD") elseif (msg[1] == SKILL_OBJECT_CMD) then OnSKILL_OBJECT_CMD (msg[2],msg[3],msg[4],msg[5]) --TraceAI ("SKILL_OBJECT_CMD") elseif (msg[1] == FOLLOW_CMD) then OnFOLLOW_CMD () --TraceAI ("FOLLOW_CMD") end end ---------------------------------------------------------------------------------------------------------------------------------- -- アイドル中に関する定義 ---------------------------------------------------------------------------------------------------------------------------------- function OnIDLE_ST () --TraceAI ("OnIDLE_ST") if NeighborFlag == false and GetTick() > NeighborTime + 10000 then -- 近くにプレイヤーが居るかどうか検知 local player = GetPlayersNearID(6) --TraceAI(" ** Player :" ..player) if player ~= 0 then NeighborFlag = true -- 存在したらtrue --TraceAI(" ** Player Exists.") end NeighborTime = GetTick() -- そのときの時間 elseif NeighborFlag and GetTick() > NeighborTime + 30000 then -- 30秒ごとに更新 NeighborFlag = false end if FriendExists then -- 友達が居たら OnBattleDistance = OnBattleDistancePlus else OnBattleDistance = OBD_Neutral end object = GetEnemy (MyID) -- 敵検索 if (object ~= 0) then if ShootingMode then -- 射撃状態で戦闘するなら MyState = SHOOTING_ST --TraceAI ("IDLE_ST -> SHOOTING_ST") else -- 通常,敵を発見したら MyState = CHASE_ST -- 追跡状態へ MyEnemy = object --TraceAI ("IDLE_ST -> CHASE_ST : ATTACKED_IN") end return end local distance = GetDistance2(MyID,MyOwner) local motion = GetV(V_MOTION,MyOwner) if OnForward and motion == MOTION_MOVE then MyState = FORWARD_ST return elseif distance > FollowDistance or distance == -1 then -- 主人の待機圏外に居る場合 if StayFlag or NowRondo then -- 停止フラグがあるとき MyState = IDLE_ST -- その場で待機(離れても戻ってこない if motion == MOTION_MOVE then -- 主人が移動したら StayFlag = false -- 停滞はやめる NowRondo = false end else MyState = FOLLOW_ST --TraceAI ("IDLE_ST -> FOLLOW_ST") return end end -- 待機状態の時に指定秒数毎にその場でくるりと1回転する if RondoFlag then local pass = os.difftime(os.time(),RondEnd)*1000 -- タイマーが発動、かつホムの残りSPが一定%を越えた状態なら旋回動作 if PatrolRest <= GetPerSP(MyID) and PatrolRest <= GetPerHP(MyID) then if pass >= PatrolInterval then Rondo(RondoPattern,RondoRadius) end end end PrintMessage(MyState,0) end -------------------------------------------------------------------------------------------------------------- -- 追従移動中に関する定義 -------------------------------------------------------------------------------------------------------------- function OnFOLLOW_ST () --TraceAI ("OnFOLLOW_ST") PrintMessage(MyState,0) local d = GetDistance2(MyID,MyOwner) if d <= FollowDistance or d > 15 then -- 主人からの距離が指定数以下のとき MyState = IDLE_ST -- 待機状態にする NowRondo = false --TraceAI ("FOLLOW_ST -> IDLW_ST") return elseif GetV(V_MOTION,MyID) == MOTION_STAND or GetV(V_MOTION,MyID) == MOTION_MOVE then -- 自分のモーションが立ちか移動のとき local pass = os.difftime(os.time(),BufferTime) if EmergencyAvoid and BufferDelay < pass and AvoidDelay < pass and RecoverDelay < GetTick() and GetV(V_MOTION,MyOwner) == MOTION_MOVE then if CheckMyType(MyType) == LIF then -- リーフなら if AutoSkillSPLimit < GetPerSP(MyID) then AutoSkill_SELF(2) end end end if MoveDelay < GetTick() then MoveDelay = GetTick() + DifferentialAccuracy MoveToNear (MyOwner, ChaseDistance) -- 主人に追従する --TraceAI ("FOLLOW_ST -> FOLLOW_ST") return end end end -------------------------------------------------------------------------------------------------------------- -- 敵ターゲットをリセットする関数 -------------------------------------------------------------------------------------------------------------- function ResetEnemy() MyState = IDLE_ST -- 状態を待機へ MyEnemy = 0 -- ターゲットを0へ ChaceTime = 0 -- 追跡時間をリセット OnBattleTime = 0 -- 戦闘時間リセット AllEnemys = {} -- 敵リストリセット AllEnemysCount = 0 -- 敵の数リセット OverlapFlag = 0 -- セル重なり回避フラグリセット AttackStCycle = 0 -- ATTACK_ST回数リセット FirstAttackFlag = true -- 初撃フラグリセット AutoSkill = ASP_Neutral -- AS確率リセット AS_SetFlag = true -- ASスキルセットフラグリセット AdditionalLevel = 0 -- AS追加レベルリセット SkillTimeOut = true -- ASタイムアウトリセット OnActive = false -- アクティブ索敵フラグリセット NowAwaying = false -- 退避中フラグリセット ManualAttack = false -- 手動攻撃フラグリセット OnlySkill = false -- スキルのみフラグリセット GuardOwnerFlag = false -- 主人護衛フラグリセット HookFlag = 0 -- フックリセット ResetWalk() -- 足跡リセット end -------------------------------------------------------------------------------------------------------------- -- 敵追跡中に関する定義 -------------------------------------------------------------------------------------------------------------- function OnCHASE_ST () --TraceAI ("OnCHASE_ST") if ChaceTime == 0 then -- 追跡時間計測 ChaceTime = GetTick() end if GetV(V_MOTION, MyID) ~= MOTION_MOVE and ChaceTime + 3000 < GetTick() then -- スタックした時の処理 追跡時間が3秒以上で疑う ResetEnemy() Move(MyID,OwnerX,OwnerY) -- スタックセルから脱出 --TraceAI("CHASE_ST -> CHASE_ST : Detect Stuck (Reset)") return end local field = math.max(TargetDistance,OnBattleDistance) local range = field + AttackDistance if GetDistance2(MyOwner,MyEnemy) > range then -- 敵が索敵範囲外になったとき ResetEnemy() -- リセットして待機状態になる MyState = IDLE_ST --TraceAI ("CHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN") return end local motion = GetV(V_MOTION, MyEnemy) -- 敵のモーション if motion == MOTION_DEAD or motion == -1 then -- 死亡モーション中の敵は追跡しない ResetEnemy() --TraceAI ("CHASE_ST -> IDLE_ST") return end if (GetDistance2(MyID,MyOwner) > OnBattleDistance and MySkill == 0) then -- 追跡中、指定距離以上ケミが離れた場合、スキル攻撃中でなければ追従 ResetEnemy() MoveToNear(MyOwner,ChaseDistance) MyState = FOLLOW_ST return end if not ManualAttack then -- 手動攻撃命令時ではないとき if not IsNotNoManner(MyID, MyEnemy) then -- 接近中に誰かターゲットを奪われたとき ResetEnemy() return end end -- Chaseの最中にスキルを使用するケース motion = GetV(V_MOTION, GetV(V_OWNER,MyID)) -- 主人のモーション if FirstAttack and FA_OnChase and FirstAttackFlag and GetDistance2(MyID, MyEnemy) > AttackDistance then if motion ~= MOTION_SIT then UseAutoSkill() FirstAttackFlag = false if HookShot and HookFlag == 0 then HookFlag = 1 end return end elseif PreventEnemyCasting and GetV(V_MOTION,MyEnemy) == MOTION_CASTING and AttackSkillDelay < GetTick() and IsMob(M_CAST,MyEnemy) == 1 then local skill = 0 local level = 0 skill,level = SetPreventSkill() if skill ~= 0 then SkillObject (MyID,level,skill,MyEnemy) -- スキル攻撃で妨害 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定(2.5秒くらい --TraceAI(" /// Prevent Casting !! ") return end end motion = GetV(V_MOTION, MyID) -- 自分のモーション if CheckMyType(MyType,1) == LIF_H then -- 進化リーフのみ if AutoBufferSkill and motion ~= MOTION_DAMAGE then -- Chaseの段階でオート強化スキル local pass = os.difftime(os.time(),BufferTime) -- スキルディレイ中でなければ if AttackSkillDelay < GetTick() and BufferDelay <= pass and AvoidDelay < pass then AutoSkill_SELF(1) return end end end local id = GetV(V_HOMUNTYPE,MyEnemy) -- 敵設定によってAttackDistance調整 if MyEnemy < 100000 and IsMob(M_SKILL,MyEnemy) >= 0 and Mob[id] then if Mob[id][M_SKILL] then if Mob[id][M_SKILL] > 6 then -- スキルのみ攻撃の場合は OnlySkill = true AttackDistance = OnBattleDistance else OnlySkill = false AttackDistance = AD_Neutral end end end -- フックフラグ判定 フック使用*フラグ0*フルスキルではない*ターゲットがまだ自分ではない if HookShot and HookFlag == 0 and not OnlySkill then HookFlag = 1 end if GetDistance2(MyID, MyEnemy) <= AttackDistance then -- 敵が攻撃射程範囲内に来たとき MyState = ATTACK_ST -- 攻撃に移る ChaceTime = 0 -- 追跡時間はリセット AttackFalseTime = GetTick() --Attack(MyID,MyEnemy) --TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN") return else MoveToNear(MyEnemy,AttackDistance) -- 敵の位置よりAttackDistanceだけ離れた位置へ if GetV(V_TARGET,MyEnemy) == MyID then HookFlag = 2 end --TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN") PrintMessage(MyState,MyEnemy,0,0,-1) return end end ----------------------------------------------------------------------------------------------- -- ターゲットをフックする関数 ----------------------------------------------------------------------------------------------- function OnHOOK_ST() local od = GetDistance2(MyOwner,MyID) local ed = GetDistance2(MyEnemy,MyID) if od > 1 then MoveToNear(MyOwner,1) return else if ed <= AttackDistance then -- 敵が射程内に寄って来たら HookTime = 0 HookFlag = 2 MyState = ATTACK_ST -- 攻撃に移る return end end if HookTime == 0 then HookTime = GetTick() else if GetTick() - HookTime > 4000 then -- 4秒以上フックしてたらタイムアウト HookTime = 0 -- フック時間リセット HookFlag = 2 -- フラグリセット MyState = ATTACK_ST -- 攻撃に移る return end end end ----------------------------------------------------------------------------------------------- -- アクティブスキルをセットする関数 -- スキルレベルとスキルを返す const : 固定スキルレベル ----------------------------------------------------------------------------------------------- function SetActiveSkill(const) local level = const local skill = 0 local enemy = GetV(V_HOMUNTYPE,MyEnemy) -- ホムンクルスの種族ごとにスキル設定 if CheckMyType(MyType) == FILIR then skill = 8009 -- ムーンライトスキル(8009)は SkillDelay = 2500 -- ディレイは2.5秒 elseif CheckMyType(MyType) == VANILMIRTH then skill = 8013 -- カプリススキル(8013)は SkillDelay = 1000+200*level -- ディレイは1000+200*Lv秒 else level = 0 skill = 0 end if skill ~= 0 and MyEnemy < 100000 and IsMob(M_SKILL,MyEnemy)==1 then -- スキルが決まったらレベル決定 local setLevel = MathThreshold(Mob[enemy][M_SKILL],6) -- 設定の絶対値 if setLevel == nil or setLevel == SKILL_AUTO then -- 個別設定が無い場合 if AutoAdjust then -- 自動調節するなら level = AdjustSkillLevel(enemy,const) -- 敵のタイプでスキルLvを調節 else -- 調節しない場合は level = const -- 設定したLvで発動 end elseif setLevel >= 1 and setLevel <= 5 then -- 個別設定がある場合 level = setLevel -- 設定レベルに end end return level,skill end ----------------------------------------------------------------------------------------------- -- 自動スキルレベル調整の関数 -- enemy : 敵種類ID const : データが無かった場合に返す固定レベル値 ----------------------------------------------------------------------------------------------- function AdjustSkillLevel (enemy,const) local record = Mob[enemy][M_RECORD] -- 戦闘記録呼び出し local level = const if record and record ~= 0 then TraceAI(" This Enemy's Record : "..record) if record < AS_threshold[1] then -- 閾値1以下なら level = 0 -- スキル使用しない elseif AS_threshold[1] <= record and record < AS_threshold[2] then -- 閾値1以上2以下なら level = 1 -- スキルLv1 elseif AS_threshold[2] <= record and record < AS_threshold[3] then -- 閾値2以上3以下なら level = 2 -- スキルLv2 elseif AS_threshold[3] <= record and record < AS_threshold[4] then level = 3 elseif AS_threshold[4] <= record and record < AS_threshold[5] then level = 4 else level = 5 end TraceAI(" -+ Adjust Level !! : "..level) return level else return level end end ----------------------------------------------------------------------------------------------- -- 自動スキル使用(アクティブ)の関数 ----------------------------------------------------------------------------------------------- function AutoSkill_OBJECT(lv,skill) local level = lv if skill ~= 0 and level ~= 0 then -- スキルがセットされていないと発動しない -- フィーリルとバニルミルト if level > 5 then -- 5以上が入っていたら5に level = 5 end if (skill == 8009 or skill == 8013) then -- 残りSPによって発動確率を調整 local nsp = GetV(V_SP,MyID) local nmsp = GetV(V_MAXSP,MyID) local AutoSkillProbability = AutoSkill + ((nsp/nmsp)^2 * 10) -- 賽を投げる local Dice = math.floor(math.random(100)) if FirstAttack and FirstAttackFlag then -- 初撃ONの場合は強制的にDice0に Dice = 0 if not Mob[GetV(V_HOMUNTYPE,MyEnemy)][M_SKILL] then level = FA_SkillLevel elseif Mob[GetV(V_HOMUNTYPE,MyEnemy)][M_SKILL] == 6 or Mob[GetV(V_HOMUNTYPE,MyEnemy)][M_SKILL] == 12 then level = FA_SkillLevel else level = Mob[GetV(V_HOMUNTYPE,MyEnemy)][M_SKILL] end FirstAttackFlag = false end if CrossFire and GetV(V_MOTION,MyOwner) == MOTION_SKILL then Dice = 0 end --TraceAI(" >>> AutoSkill ---- "..AutoSkillProbability.." Dice : "..Dice) if (AutoSkillProbability > Dice) then --TraceAI (" !! Auto Attack Skill ") SkillObject (MyID,level,skill,MyEnemy) -- スキル攻撃 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定 end end end end ----------------------------------------------------------------------------------------------- -- 自動スキル使用(ブースト)の関数 -- case:1 フリットムーブ,ブラッドラスト,メンタルチェンジ,ディフェンス -- case:2 緊急回避 -- case:3 オーバードスピード ----------------------------------------------------------------------------------------------- function AutoSkill_SELF(case) if CheckMyType(MyType) == FILIR then -- フィーリル if case == 1 then MySkill = 8010 -- フリットムーブ(8010)は MySkillLevel = BufferLevel -- 指定Lvで常時発動 BufferDelay = 65 - BufferLevel * 5 -- 持続時間計算 BufferTime = os.time() elseif case == 3 then MySkill = 8011 -- オーバードスピード(8011)は MySkillLevel = BufferLevel2 -- 指定Lvで発動 BufferDelay2 = 65 - BufferLevel2 * 5 -- 持続時間計算 BufferTime2 = os.time() end elseif CheckMyType(MyType) == AMISTR then -- アミストル if BloodLust then -- ブラッドラストの方を使う場合 MySkill = 8008 -- ブラッドラスト(8008)は MySkillLevel = 3 -- 指定Lvで常時発動 BufferDelay = 3*300 -- 持続時間計算(Lv*5分) else MySkill = 8006 -- ディフェンス(8006)は MySkillLevel = BufferLevel -- 指定Lvで常時発動 BufferDelay = 45 - BufferLevel*5 -- 持続時間計算 end BufferTime = os.time() elseif CheckMyType(MyType) == LIF then -- リーフ if case == 2 then MySkill = 8002 -- 緊急回避(8002)は MySkillLevel = EA_Level -- 指定Lvで常時発動 if EA_Level == 1 then -- Lv1では AvoidDelay = 40 -- 持続時間の40秒まで else -- それ以外はディレイの方が長いので AvoidDelay = 35 -- ディレイ終了の35秒まで end else if CheckMyType(MyType,1) == LIF_H then -- 進化リーフ MySkill = 8004 -- メンタルチェンジ(8004)は MySkillLevel = 3 -- 固定Lv3で常時発動 BufferDelay = 3*120 - 60 -- 持続時間計算 MentalChange = true end end BufferTime = os.time() else -- それ以外 MySkill = 0 MySkillLevel = 0 AutoBufferSkill = false -- 自動的に機能オフに end if MySkill ~= 0 then SkillObject (MyID,MySkillLevel,MySkill,MyID) -- スキル使用 --TraceAI ("ATTACK_ST -> ATTACK_ST : AutoSkill_SELF"..BufferDelay) AttackSkillDelay = 1000 + GetTick() -- 1秒程のディレイ MySkill = 0 MySkillLevel = 0 end end ----------------------------------------------------------------------------------------------- -- 自動スキル使用(回復)の関数 -- カオティックベネディクション,治癒の手 ----------------------------------------------------------------------------------------------- function AutoSkill_HEAL () local object = 0 if CheckMyType(MyType) == VANILMIRTH then -- バニル MySkill = 8014 -- カオティックベネディクション(8014)は MySkillLevel = RecoverLevel -- 指定Lvで発動 RecoverDelay = GetTick() + 1000 -- ディレイ設定 object = MyID -- 対象指定 elseif CheckMyType(MyType) == LIF then -- リーフ MySkill = 8001 -- 治癒の手助け(8001)は MySkillLevel = RecoverLevel -- 指定Lvで発動 RecoverDelay = GetTick() + 2500 -- ディレイ設定 object = MyID -- 対象指定 else -- それ以外 MySkill = 0 MySkillLevel = 0 end if MySkill ~= 0 then if MySkill == 8014 then -- スキルがカオティックベネディクションなら local actors = GetActors() -- 画面内オブジェクト取得 local enemyExists = false -- Mob存在フラグ for i,v in ipairs(actors) do if IsMonster(v) == 1 then -- Mobが居たら enemyExists = true -- フラグONにしてループを抜ける break end end if enemyExists then -- 居たらスキルを使わないで抜ける MySkill = 0 MySkillLevel = 0 return end end if FirstAid then -- 初めてこの関数が回ってきたときの BeforeHP = GetV(V_HP,GetV(V_OWNER,MyID)) -- 主人のHP取得 --TraceAI (" ---- OwnerHP : "..BeforeHP) end SkillObject (MyID,MySkillLevel,MySkill,object) -- スキル使用 --TraceAI (" ** AutoRecovery!!") ReturnFlag = true if MySkill == 8001 and FirstAid == false then -- スキルが治癒の手なら(2回目以降 local afterHP = GetV(V_HP,GetV(V_OWNER,MyID)) -- 主人のHP取得 if afterHP == BeforeHP then -- HPが回復していなかったら赤スリムを持ってないとみなす AutoRecoverSkill = false -- オートリカバーOFF --TraceAI (" -- AutoRecovery OFF") else BeforeHP = afterHP end end MySkill = 0 MySkillLevel = 0 FirstAid = false end end ----------------------------------------------------------------------------------------------- -- 自動スキルに関する関数 ----------------------------------------------------------------------------------------------- function UseAutoSkill() if (AutoSkillSPLimit < GetPerSP(MyID) and IsMob(M_SKILL,MyEnemy) >= 0) then local motion = GetV(V_MOTION,MyID) -- モーション取得(被ダメモーション以外ならスキル発動) if AutoBufferSkill and SkillTimeOut and motion ~= MOTION_DAMAGE and MyState == ATTACK_ST then -- オート強化スキル local pass = os.difftime(os.time(),BufferTime) -- スキルディレイ中でなければ if AttackSkillDelay < GetTick() and BufferDelay <= pass and AvoidDelay < pass then if BufferLimit <= AllEnemysCount then -- 敵の数で AutoSkill_SELF(1) end end end -- * アクティブスキル関連 if AS_SetFlag and (AutoAttackSkill or FirstAttack) then -- スキルセットは1回だけでいい A_SkillLevel,A_Skill = SetActiveSkill(SetSkillLevel) -- スキルをセット local enemy = GetV(V_HOMUNTYPE,MyEnemy) if MyEnemy < 100000 and IsMob(M_AS,MyEnemy) == 1 then AutoSkill = AutoSkill + Mob[enemy][M_AS] -- 基本確率設定 end if OnlySkill then AutoSkill = 100 end AS_SetFlag = false -- スキル調整終了 TraceAI(" ++ AutoSkillProbability : "..AutoSkill.." AS Level! : "..A_SkillLevel+AdditionalLevel) end if motion ~= MOTION_DAMAGE then if AutoAttackSkill or FirstAttackFlag then if AttackSkillDelay < GetTick() and SkillTimeOut then -- 使用フラグとディレイのチェック AutoSkill_OBJECT(A_SkillLevel+AdditionalLevel,A_Skill) -- オート攻撃スキル end end end end end ----------------------------------------------------------------------------------------------- -- 敵と座標が重なってる時に移動させる関数 ----------------------------------------------------------------------------------------------- function AvoidOverlap() local x,y = GetV(V_POSITION,MyID) if OverlapFlag < AwayCount and MoveDelay < GetTick() then OverlapFlag = OverlapFlag + 1 local dx, dy = 0,0 if AwayType == 0 then -- 回避タイプ0:絶対座標 dx, dy = AvoidDirection[OverlapFlag].x*AwayStep, AvoidDirection[OverlapFlag].y*AwayStep else local ox,oy = GetV(V_POSITION,MyOwner) -- 主人の座標 local ex,ey = GetV(V_POSITION,MyEnemy) -- 敵の座標 dx,dy = VectorCheck(ox,oy,ex,ey) -- 主人から見た敵の方向 if dx ==0 and dy == 0 then -- どっちも0(主人,ホム,敵全て同座標のケース) dx, dy = AvoidDirection[OverlapFlag].x*AwayStep, AvoidDirection[OverlapFlag].y*AwayStep else dx, dy = dx*AwayStep, dy*AwayStep end end Move(MyID,x+dx,y+dy) -- 目標点へ移動 MoveDelay = 140*AwayStep + GetTick() -- 移動ディレイ計算 --TraceAI ("Overlapped!! count:"..OverlapFlag) return end end ----------------------------------------------------------------------------------------------- -- 指定の敵に対し詠唱妨害する関数 ----------------------------------------------------------------------------------------------- function PreventCasting(enemys) local preventTarget = 0 local castingList = {} -- リストがあれば castingList = GetMotionActors(enemys,{MOTION_CASTING}) -- 詠唱中のMob castingList = GetEnemys(castingList) preventTarget = GetNearActorID(MyID, castingList) if preventTarget ~= 0 and preventTarget ~= MyEnemy then local skill = 0 local level = 0 skill,level = SetPreventSkill() if skill ~= 0 and AttackSkillDelay < GetTick() and GetV(V_SP,MyID) > 30 then -- スキル可能かつSP余り有り,ディレイがなければ SkillObject (MyID,level,skill,preventTarget) -- スキル攻撃で妨害 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定(2.5秒くらい --TraceAI(" /// Prevent Casting !! ") elseif GetDistance2(MyID,preventTarget) < 3 then -- スキルなしまたはディレイ中で距離が2以内なら Attack(MyID,preventTarget) -- 通常攻撃で妨害 ReturnFlag = true end end end function SetPreventSkill() local skill = 0 local level = 0 -- 詠唱妨害するためのスキルをセット if CheckMyType(MyType) == FILIR then skill = 8009 -- ムーンライトスキル(8009)は SkillDelay = 2500 -- ディレイは2.5秒 level = PreventSkillLevel -- Lv1で発動 elseif CheckMyType(MyType) == VANILMIRTH then skill = 8013 -- カプリススキル(8013)は SkillDelay = 1200 -- ディレイは1.2秒 level = PreventSkillLevel -- Lv1で発動 else level = 0 skill = 0 end return skill,level end ----------------------------------------------------------------------------------------------- -- 敵のチェックをする関数 ----------------------------------------------------------------------------------------------- function CheckEnemy(list,num) local enemys = list local actEscape = {} local escIndex = 0 local castOn = {} local castIndex = 0 local boostOn = {} local boostIndex = 0 local preTarget = 0 local o_enemy = {} local oeIndex = 0 if GetV(V_TARGET,MyEnemy) then if GetV(V_TARGET,MyEnemy) == 0 then enemys[num+1] = MyEnemy end end for i,v in ipairs(enemys) do local id = GetV(V_HOMUNTYPE,v) if id > 0 and v < 100000 then if IsMob(M_ACT,v) == -3 then -- 退避設定なら escIndex = escIndex + 1 actEscape[escIndex] = v end if IsMob(M_CAST,v) == 1 then -- 詠唱妨害ONなら castIndex = castIndex + 1 castOn[castIndex] = v end if IsMob(M_BOOST,v) == 1 then -- 特殊強化ONなら boostIndex = boostIndex + 1 boostOn[boostIndex] = v end if IsMob(M_RANK,v) == 1 then -- 優先度ありなら local rank = MathThreshold(Mob[id][M_RANK],20) if rank then -- 優先度10以上で条件 if preTarget == 0 then preTarget = v else local pRank = MathThreshold(Mob[GetV(V_HOMUNTYPE,preTarget)][M_RANK],20) if rank > pRank then preTarget = v end end end end if GetV(V_TARGET,v) == MyOwner then -- 主人をターゲットしている敵 oeIndex = oeIndex + 1 o_enemy[oeIndex] = v end -- チェックここまで end end if actEscape[1] then -- 退避Mobが居たら EscapeFromDanger(actEscape) -- 特定退避行動 return end if PreventEnemyCasting and castOn[1] then -- 詠唱妨害ONなら PreventCasting(castOn) -- 詠唱妨害する end if preTarget ~= 0 then -- ターゲット変更 local preRank = MathThreshold(Mob[GetV(V_HOMUNTYPE,preTarget)][M_RANK],20) if preRank <= 15 then -- 15以下なら local o_target = GetV(V_TARGET,MyOwner) if o_target > 0 and IsMonster(o_target) == 1 and not FollowOwnerTargetting then local o_rank = MathThreshold(Mob[GetV(V_HOMUNTYPE,o_target)][M_RANK],20) if o_rank > 10 then -- 主人のターゲットの敵優先度が11以上 ChangeTarget(MyOwner) -- 主人のタゲに変更 end end else local nowRank = 20 local nID = GetV(V_HOMUNTYPE,MyEnemy) if nID > 0 then nowRank = MathThreshold(Mob[nID][M_RANK],20) end if preRank > nowRank then ChangeTarget(preTarget) -- 最優先の敵へターゲット変更 end end else -- 最優先ターゲットが無い状況では主人のターゲットを見る if GuardOwner and GetV(V_TARGET,MyEnemy) ~= MyOwner then local ownersEnemy = GetNearActorID(MyID,o_enemy) -- 一番近い相手 --TraceAI(" --------change owners target : "..ownersEnemy) if ownersEnemy ~= 0 then GuardOwnerFlag = true ChangeTarget(ownersEnemy) else GuardOwnerFlag = false end end end if boostOn[1] then local motion = GetV(V_MOTION,MyID) -- モーション取得(被ダメモーション以外ならスキル発動) if motion ~= MOTION_DAMAGE then if AutoSkillSPLimit < GetPerSP(MyID) then local pass = 0 local delay = 0 if CheckMyType(MyType) == FILIR then -- フィーリルの場合は pass = os.difftime(os.time(),BufferTime2) -- 専用の開始時間 delay = BufferDelay2 -- 専用のディレイ管理 else -- それ以外は pass = os.difftime(os.time(),BufferTime) delay = BufferDelay -- 通常と同じディレイ管理 end if AttackSkillDelay < GetTick() and delay <= pass and AvoidDelay < pass then -- スキルディレイ中でなければ -- 特殊強化のレベル設定(一番高いレベルで発動) local level = 0 for i,v in ipairs(boostOn) do local id = GetV(V_HOMUNTYPE,v) if level < Mob[id][M_BOOST] then level = Mob[id][M_BOOST] end end BufferLevel2 = level AutoSkill_SELF(3) -- オート特殊強化スキル return end end end end end ----------------------------------------------------------------------------------------------- -- 特定の敵から逃げる関数 ----------------------------------------------------------------------------------------------- function EscapeFromDanger(enemys) local nearest = 0 nearest = GetNearActorID(MyID,enemys) -- 一番近い相手 if GetV(V_TARGET,nearest) == MyID then if GetDistance2(MyID,nearest) < EscapeRange then -- ターゲットが自分かつ距離が近い MoveBackward (nearest,EscapeRange) --TraceAI(" Away From Danger !!!") NowAwaying = true end else NowAwaying = false end end ----------------------------------------------------------------------------------------------- -- 戦闘中にターゲットをidの敵に変更する関数 ----------------------------------------------------------------------------------------------- function ChangeTarget(id) if IsPlayer(id) == 1 or IsHomunculus(id) == 1 then -- id がプレイヤーまたはホムンクルスの時 local target = GetV(V_TARGET,id) -- すでに消えたIDはIsMonster==0 if target > 0 and IsMonster(target) == 1 and GetDistance2(MyOwner,target) < 4 and MyEnemy ~= target and IsMob(M_ACT,target) >= 0 then local preRank = MathThreshold(Mob[GetV(V_HOMUNTYPE,target)][M_RANK],20) -- ターゲットの優先度 local nowRank = 0 local nID = GetV(V_HOMUNTYPE,MyEnemy) if nID > 0 then nowRank = MathThreshold(Mob[nID][M_RANK],20) -- 現在の敵の優先度 end if nowRank <= 15 or nowRank <= preRank then -- 優先度15以下なら ResetEnemy() MyEnemy = target MyState = ATTACK_ST PrintMessage(MyState,MyEnemy,0,0,0) end end elseif id > 0 and IsMonster(id) == 1 then -- idがMobの時はidをターゲット if MyEnemy ~= id and IsMob(M_ACT,id) >= 0 then ResetEnemy() MyEnemy = id MyState = ATTACK_ST PrintMessage(MyState,MyEnemy,0,0,0) end end end -- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- -- 攻撃中に関する定義 -- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- function OnATTACK_ST () --TraceAI ("OnATTACK_ST") AttackStCycle = AttackStCycle +1 -- ATTACK_STが回った回数 if FollowOwnerTargetting and not GuardOwnerFlag then -- 主人のターゲットを追従 ChangeTarget(MyOwner) end CheckEnemy(AllEnemys,AllEnemysCount) -- 敵への行動チェック local id = GetV(V_HOMUNTYPE,MyEnemy) -- MyEnemyの種ID -- 敵IDと数管理およびトリガー発動 if MathMod(AttackStCycle,3) == 1 then -- サイクルの3回に1回で AllEnemys = GetActors() -- 自分たちの敵を常に監視 AllEnemys = GetOurEnemys(AllEnemys) local count = TableSize(AllEnemys) if AllEnemysCount ~= count then -- 戦闘開始または敵の数が変わるたびに if count >= EnemyBorder_N then TriggerControl(TRIGGER_ON,MANY_ENEMYS,MyEnemy) -- トリガーチェック else TriggerControl(TRIGGER_OFF,MANY_ENEMYS,MyEnemy) -- トリガーチェック end end AllEnemysCount = count if AttackStCycle == 1 then if IsMob(M_RECORD,MyEnemy) == 1 then -- レコードがあれば if Mob[id][M_RECORD] > EnemyBorder_T then TriggerControl(TRIGGER_PING,TOUGH_ENEMY) -- トリガーチェック end end end end local od = GetDistance2(MyID,MyOwner) -- 主人と自分の距離 if od > OnBattleDistance then -- 戦闘中、指定距離以上ケミが離れた場合、それに追従 MoveToNear(MyOwner,ChaseDistance) ResetEnemy() MyState = FOLLOW_ST return elseif HookFlag == 1 and GetV(V_TARGET,MyEnemy) == MyID and od > AttackDistance then MyState = HOOK_ST return end if HateOverlap and not SPR_Flag and not NowAwaying then if (GetDistance2(MyID,MyEnemy) == 0 or GetDistance2(MyID,MyOwner) == 0) then if OverlapFlag < AwayCount and OnBattleTime > 0 then AvoidOverlap() -- 戦闘時,敵か主人と座標が被った場合は移動 return end elseif OverlapFlag == AwayCount then -- 重なってなければ OverlapFlag = 0 -- フラグリセット end end local motion = GetV(V_MOTION, MyEnemy) local i_motion = GetV(V_MOTION, 100000 - MyEnemy) if motion == MOTION_DEAD or motion == -1 or i_motion == MOTION_DEAD then -- 攻撃対象が死んだとき if MyEnemy < 100000 then -- 対象がMobなら LostEnemy = MyEnemy --TraceAI (" ! LostEnemy : "..LostEnemy) if RecordBattleTime and not NowAwaying then RecordDist(100000 - MyEnemy) -- 戦闘記録( IDは死亡時点で10万ルールが適用されてる end end ResetEnemy() -- 敵情報をリセット --TraceAI ("ATTACK_ST -> IDLE_ST") return end if IsOutOfSight(MyID,MyEnemy) then -- 敵が索敵範囲外に出たとき ResetEnemy() -- 敵情報をリセット return end if NowAwaying or ReturnFlag then -- 退避対象敵が居る場合 return end if not IsNotNoManner(MyID, MyEnemy) and not ManualAttack then -- 横殴りチェック ResetEnemy() return end if GetDistance2(MyID, MyEnemy) > AttackDistance then -- 敵が攻撃射程外に出たとき MyState = CHASE_ST -- 追跡状態へ MoveToNear(MyEnemy,AttackDistance) --TraceAI ("ATTACK_ST -> CHASE_ST : ENEMY_OUTATTACKSIGHT_IN") return end if OnBattleTime == 0 then OnBattleTime = GetTick() -- 戦闘時間計測開始 end if OnBattleTime > 0 then -- 共闘モードの際 if GetTick() - OnBattleTime > BonusTime and AllEnemysCount < BonusBorder then if IsMob(M_RANK,MyEnemy) == 1 then if Mob[id][M_RANK] > 20 then BonusFlag = true return end elseif BonusMode then BonusFlag = true return else BonusFlag = false end else BonusFlag = false end end -- 通常攻撃 if not OnlySkill then Attack(MyID,MyEnemy) -- 攻撃 AttackedFlag = true -- 攻撃完了 if MovingCancel then local nx,ny = GetV(V_POSITION,MyID) Move(MyID,nx,ny) -- ゼロ移動 Attack(MyID,MyEnemy) -- 攻撃 end end -- +++ 戦闘時間記録によるAS確率調整 +++ -- if MyEnemy < 100000 and IsMob(M_RECORD,MyEnemy) == 1 and not OnlySkill then if AutoAttackSkill and StopAS then if OnBattleTime > 0 and Mob[id][M_RECORD] > 0 then -- 戦闘中かつレコードのある敵 local now = GetTick() - OnBattleTime -- 現在の経過時間 -- レコードの90%以上経過していたら or 平均戦闘時間が指定値以下の相手なら(雑魚にはスキル使わない if now > Mob[id][M_RECORD] * (ASCutTimeRate/100) then SkillTimeOut = false --TraceAI (" /// Stop Auto Skill") end end end end -- +++ オートスキル +++ -- if MySkill == 0 then -- オートスキルの発動条件 local o_motion = GetV(V_MOTION,GetV(V_OWNER,MyID)) if o_motion ~= MOTION_SIT then -- 主人が座ってたらスキルは使わない UseAutoSkill() end else SkillObject (MyID,MySkillLevel,MySkill,MyEnemy) AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定(2.5秒くらい MySkill = 0 end -- +++ 自動友達登録 +++ -- AutoAddFriend() PrintMessage(MyState,MyEnemy) end ------------------------------------------------------------------------- -- 平均戦闘時間を記録する ------------------------------------------------------------------------- function RecordDist(enemy) local enemy = GetV(V_HOMUNTYPE,enemy) local diftime = GetTick() - OnBattleTime if enemy > 0 then if diftime < 60000 then if KillCount[enemy] == nil then -- 今日初めて戦う相手なら KillCount[enemy] = 1 DistTime[enemy] = diftime else KillCount[enemy] = KillCount[enemy] + 1 DistTime[enemy] = DistTime[enemy] + diftime end else return end --TraceAI(" -- Dist : "..enemy.." KillCount :"..KillCount[enemy].." TotalTime : "..DistTime[enemy]) local kc = KillCount[enemy] if MathMod(kc,RecCount) == 0 then -- 剰余計算.10回ごとに集計 if not Mob[enemy] then Mob[enemy] = {3,6,0,0,0,0,0} end if Mob[enemy][M_RECORD] == nil or Mob[enemy][M_RECORD] == 0 then -- 初めて記録される場合 Mob[enemy][M_RECORD] = math.floor(DistTime[enemy] / KillCount[enemy]) else -- Mob[enemy][M_RECORD] = math.floor((Mob[enemy][M_RECORD] + (DistTime[enemy] / KillCount[enemy]))/2) -- 前回の記録と足して2で割る end if Mob[enemy][M_RECORD] > 60000 then -- 60秒以上の記録は無効 Mob[enemy][M_RECORD] = 0 end KillCount[enemy] = nil -- カウントしなおし SaveRecord(MobFilename) -- 自動保存 TraceAI(" -- Recoad : "..enemy.." KillCount :"..kc.." AverageTime : "..Mob[enemy][M_RECORD]) end end end -- ------------------------------------------------------ -- -- レコードをロードする -- ------------------------------------------------------ -- function LoadRecord(file) local filename = SaveDir..file..".lua" local oldfilename = SaveDir..file..".ini" local fp = io.open(filename,"r") -- ファイルを開く local index = 1000 -- MobのIDは1000から if (fp ~= nil) then require (filename) --TraceAI(" ! ReadFile : "..filename) fp:close() else -- ファイルが無い場合は新規作成 --TraceAI(" NewFile "..filename) fp = io.open(oldfilename,"r") -- 旧ファイルからのコンバート while index < 3000 do index = index + 1 -- Mob配列のルールメモ:[1]Mobに対する反応 [2]スキル使用判断 [3]AS個別基本確率 [4]詠唱妨害判断 [5]特殊強化判断 [6]敵優先度 [7]平均戦闘時間 Mob[index] = {3,6,0,0,0,0,0} if fp then local line = fp:read() local p = 0 if line then for n in string.gfind(line, "%d+") do p = p + 1 if tonumber(n) then Mob[index][p] = tonumber(n) else Mob[index][p] = 0 end end end end if 1078 <= index and index <= 1085 then -- 草キノコ Mob[index][M_ACT] = ACT_IGNORE -- 非反撃の値を入れる Mob[index][M_SKILL] = SKILL_NO -- スキル使わない elseif index == 1555 or index == 1575 or index == 1579 or index == 1589 or index == 1590 then Mob[index][M_ACT] = ACT_NO -- 無視の値を入れる end end SaveRecord(MobFilename) end -- 一応PC分のIDも入れておく index = 0 while index <= 30 do Mob[index] = {3,6,0,0,0,0,0} index = index + 1 end index = 4001 while index <= 4050 do Mob[index] = {3,6,0,0,0,0,0} index = index + 1 end end ---------------------------------------------------------- -- レコードをセーブする ---------------------------------------------------------- function SaveRecord(file) local filename = SaveDir..file..".lua" local fp fp = io.open(filename,"w") if (fp ~= nil) then for i,v in pairs(Mob) do if not v[1] then v[1] = 3 end if not v[2] then v[2] = 6 end if not v[3] then v[3] = 0 end if not v[4] then v[4] = 0 end if not v[5] then v[5] = 0 end if not v[6] then v[6] = 0 end if not v[7] then v[7] = 0 end if v[1]~=3 or v[2]~=6 or v[3]~=0 or v[4]~=0 or v[5]~=0 or v[6]~=0 or v[7]~=0 then fp:write(string.format("Mob[%d]={ ",i)) fp:write(string.format("%d , %d , %d , %d , %d , %d , %d }\n",v[1],v[2],v[3],v[4],v[5],v[6],v[7])) -- 書き込み end end fp:close() --TraceAI(" ! Save Record : "..filename) end end ------------------------------------------- -- 移動命令を受けて移動する状態 ------------------------------------------- function OnMOVE_CMD_ST () TraceAI ("OnMOVE_CMD_ST") local x, y = GetV (V_POSITION,MyID) local ox, oy = GetV (V_POSITION,MyOwner) if (x == MyDestX and y == MyDestY) then -- DESTINATION_ARRIVED_IN if MoveToStay then ResetEnemy() MyState = FOLLOW_CMD_ST StayFlag = true else if ox == OwnerX and oy == OwnerY and GetDistance(x,y,ox,oy) > FollowDistance then StayFlag = true else StayFlag = false end MyState = LastState end MyDestX,MyDestY = 0,0 -- 目標地点リセット end Move (MyID,MyDestX,MyDestY) -- 移動 end ------------------------------------------- -- Alt+Tで休息状態に居るとき ------------------------------------------- function OnFOLLOW_CMD_ST () --TraceAI ("OnFOLLOW_CMD_ST") PrintMessage(MyState,0) local d = GetDistance2(MyID,MyOwner) if d <= FollowDistance or d > 15 then -- 指定セル以下の距離なら return end if (GetV(V_MOTION,MyID) == MOTION_STAND or GetV(V_MOTION,MyID) == MOTION_MOVE) then -- 自分のモーションが立ちか移動のとき if MoveDelay < GetTick() and StayFlag==false then MoveDelay = GetTick() + DifferentialAccuracy MoveToNear (MyOwner, ChaseDistance) -- 主人に追従する --TraceAI ("FOLLOW_ST -> FOLLOW_ST") return end end end ------------------------------------------------------------------------------------- -- 対象がプレイヤーであるかどうかを確かめる関数 ------------------------------------------------------------------------------------- function IsPlayer (id) if id < 100000 then -- id 100000以下はMob return 0 else return 1 end end ------------------------------------------------------------------------------------- -- 対象が友達であるかどうかを確かめる関数 ------------------------------------------------------------------------------------- function IsFriend(id) if id >= 100000 then -- idが100000以上ならプレイヤー if FriendList[id] == GetV(V_HOMUNTYPE,id) then return 1 end else -- それ以下ならホム if HomFriendList[id] == GetV(V_HOMUNTYPE,id) then return 1 end end return 0 -- 無ければ0を返す end ------------------------------------------------------------------------------------- -- 対象がホムンクルスであるかどうかを確かめる関数 ------------------------------------------------------------------------------------- function IsHomunculus(id) if (id >= 100000) then -- id 100000以上はプレイヤー return 0 end id = GetV(V_HOMUNTYPE,id) if id >=1 and id <= 16 then return 1 else return 0 end end ------------------------------------------------------------------------------------- -- Mobに対する挙動判定関数 ------------------------------------------------------------------------------------- function IsMob(case,id) if case==nil or id ==nil or id <= 0 or IsPlayer(id) == 1 or IsHomunculus(id) == 1 then -- プレイヤーまたはホムのIDが入ってきたら return 0 -- 0を返して抜ける end if id and id < 100000 then id = GetV(V_HOMUNTYPE,id) -- 種別ID取得 if not Mob[id] then -- 未設定のMob Mob[id] = {3, 6, 0, 0, 0, 0, 0} -- 初期値を入れる end if id > 1000 and Mob[id][case] then -- その配列の値が存在するかチェック if case == M_ACT then -- Mobに対する反応に関する判定 if Mob[id][case] == ACT_IGNORE then -- 無視する敵は-1を返す return -1 elseif Mob[id][case] == ACT_NO then -- 一切攻撃しない敵は-2を返す return -2 elseif Mob[id][case] == ACT_RAID then -- 先制攻撃する敵には2を返す return 2 elseif Mob[id][case] == ACT_ESCAPE then -- 逃げる敵には-3を返す return -3 else -- どれも当てはまらない場合は0を return 0 end elseif case == M_SKILL then -- スキルの使用に関する判定 if Mob[id][case] == SKILL_NO then -- スキルを使わない場合-1を返す return -1 else -- 使う場合は0を返す return 1 end elseif case == M_AS then -- AS上乗せ確率 if Mob[id][case] ~= 0 then return 1 end elseif case == M_CAST then -- 詠唱妨害に関する判定 if Mob[id][case] == CAST_ON then -- 詠唱妨害する敵は1を返す return 1 else return 0 end elseif case == M_BOOST then if Mob[id][case] > BOOST_OFF and Mob[id][case] <= 5 then -- 特殊強化する敵に関する判定 return 1 -- 設定が1〜5なら1を返す else return 0 end elseif case == M_RANK then if Mob[id][case] ~= 0 then return 1 end elseif case == M_RECORD then if Mob[id][case] ~= 0 then return 1 end else return 0 end end end return 0 end ------------------------------------------------------------------------------------- -- 対象が画面内に存在するかどうかを確かめる関数 -- 存在する→1が返る 存在しない→0が返る ------------------------------------------------------------------------------------- function IsInSight(id) local actors = GetActors() for i,actor in ipairs(actors) do if actor == id then return 1 end end return 0 -- 画面内にidが居れば1、なければ0 end ------------------------------------------------------------------------------------- -- 対象が植物であるかどうかを確かめる関数 草キノコ→1 その他→0 ------------------------------------------------------------------------------------- function IsPlant(id) id = GetV(V_HOMUNTYPE,id) if 1078 <= id and id <= 1085 then return 1 else return 0 end end -------------------------------------------------------------------------------------- -- 横殴り防止関数 idのターゲットが自分、主人、友達のどれかであればtrueが返る -------------------------------------------------------------------------------------- function IsNotNoManner( myid, id ) local ans = true local target = GetV(V_TARGET, id) -- idのターゲット if target == 0 or ( target == myid or target == GetV(V_OWNER, myid) or IsFriend(target) == 1) then ans = true elseif target < 100000 and GetV(V_HOMUNTYPE,target) > 1000 then -- idのターゲットが他Mobなら ans = true elseif IsInSight(target) == 0 then -- idの ターゲットが画面外ならtrue ans = true else return false end if OnActive then -- アクティブ索敵中のみ -- ターゲット周囲のプレイヤーチェック local players = GetActors() players = GetInRangeActors(players, id, OthersRange) -- ターゲットした敵の周囲のオブジェクト players = GetPlayers(players) -- プレイヤーまたはホム players = GetNotFriend(players) -- 友達以外 if players[1] then ans = false LostEnemy = 100000 - id -- この敵はしばらくターゲットできない --TraceAI(" !! Near Other Player...") else LostEnemy = 0 end end return ans end --------------------------------------------------------------------------------------------------------------------------------- -- 主人の敵を取得する関数 --------------------------------------------------------------------------------------------------------------------------------- function GetEnemy (myid) local result = 0 local dist = OnBattleDistance + AttackDistance -- 索敵範囲 for index,select in ipairs(EnemyPriority) do if select == 0 then elseif select == 1 then result = GetMyEnemyID(dist) -- 自分を攻撃している敵(敵→ホム) elseif select == 2 then result = GetOwnerEnemyID(dist) -- 主人を攻撃している敵(敵→主人) elseif select == 3 then result = GetOwnerAttackEnemyID(dist) -- 主人が攻撃している敵(主人→敵) elseif select == 4 and FriendExists then result = GetFriendEnemyID(dist) -- 友達を攻撃している敵(敵→友達) elseif select == 5 and FriendExists then result = GetFriendAttackEnemyID(dist) -- 友達が攻撃している敵(敵→友達) elseif select == 6 then result = GetRankedEnemyID(dist,1) -- 自分たちを攻撃する中で最も弱い敵 elseif select == 7 then result = GetRankedEnemyID(dist,2) -- 自分たちを攻撃する中で最も強い敵 elseif select == 8 then result = GetOwnerOutRangeEnemyID(dist) -- 主人を攻撃する遠距離の敵(遠距離敵→主人) elseif select == 9 then result = GetOutRangeEnemyID(dist) -- 自分を攻撃する遠距離の敵(遠距離敵→自分) elseif select == 10 then result = GetRankedEnemyID(dist,3) -- 優先順位設定のある敵のうち最も優先度の高い敵 end if result ~= 0 then -- 敵が見つかり次第抜ける return result end end if GetPerHP(MyID) >= ActiveHP and GetPerSP(MyID) >= ActiveSP and MyState ~= SHOOTING_ST then if ActiveFlag or HalfActive then local range = math.min(TargetDistance,OnBattleDistance) -- 索敵範囲と戦闘範囲どちらか小さいほう if ActiveRange then range = AdjustActiveRange(range) end result = GetFreeEnemyID(range,RankedEnemySelect) -- フリーターゲットの敵(アクティブ時のみ) end end return result end --------------------------------------------------------------------------------------------------------------------------------- -- 他人との距離からアクティブ索敵範囲を調整する関数 --------------------------------------------------------------------------------------------------------------------------------- function AdjustActiveRange(range) local player = GetPlayersNearID(14) -- 一番近い他人プレイヤー if player ~= 0 then -- プレイヤーが居たら local dist = math.min(GetDistance2(MyID,player),GetDistance2(MyOwner,player)) -- その人と自分又は主人との距離(近いほう if dist > LimitRangeDist then -- 制限した距離以上なら local decRate = TargetDistance*3 range = math.floor(range*(dist/decRate)) -- 索敵距離計算 else -- 制限した距離内だと range = -1 -- 索敵しないことに end end return range end ------------------------------------------------------------------------------------------------------------------------------------------- --指定したターゲットを持っている相手を返す function GetTargetActors(argActors, targetID) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if targetID ~= 0 then if GetV(V_TARGET, actorID) == targetID then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end else -- 対象のターゲットが0(フリーの敵)を探す local target = GetV(V_TARGET, actorID) -- 実際の対象のターゲット取得 if target == targetID or target == -1 or IsInSight(target)==0 then -- ターゲットが0か-1,またはターゲットが画面外 resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------- --主人、自分、友達のいずれかをターゲットしている敵 function GetOurEnemys(argActors) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if IsMonster(actorID) == 1 and actorID ~= (100000 - LostEnemy) and GetV(V_MOTION,actorID) ~= MOTION_DEAD then -- Mobであるかつ死亡してない if GetV(V_TARGET, actorID) == MyID or GetV(V_TARGET, actorID) == MyOwner then -- ターゲットが主人か自分 resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID elseif IsFriend(GetV(V_TARGET, actorID)) == 1 then -- または、ターゲットIDが友達リストの中にある resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------- --指定したモーションを取っている相手を返す function GetMotionActors(argActors, argMotions) local resultActors = {} local resultActorsIndex = 0 for argMotions, motion in ipairs(argMotions) do for argActorsIndex, actorID in ipairs(argActors) do if GetV(V_MOTION, actorID) == motion then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --指定したモーションを取っている相手以外を返す function GetNotMotionActors(argActors, argMotions) local resultActors = {} local resultActorsIndex = 0 for argMotions, motion in ipairs(argMotions) do for argActorsIndex, actorID in ipairs(argActors) do if GetV(V_MOTION, actorID) ~= motion then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --攻撃可能な相手を返す function GetEnemys(argActors) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if IsMonster(actorID) == 1 and actorID ~= (100000 - LostEnemy) and actorID ~= MyID and actorID ~= MyOwner then if OnActive then -- アクティブ索敵時とノンアクティブ索敵時は別判定を if IsMob(M_ACT,actorID) >= 0 then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end else if IsMob(M_ACT,actorID) >= 0 or IsMob(M_ACT,actorID) == -3 then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------- --先制攻撃可能な相手を返す function GetRaidEnemys(argActors) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if IsMonster(actorID) == 1 and IsMob(M_ACT,actorID) == 2 and actorID ~= (100000 - LostEnemy) then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------- --主人以外のプレイヤーキャラクターを返す function GetPlayers(argActors) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if IsPlayer(actorID) == 1 or IsHomunculus(actorID) == 1 then -- PCまたはホム if actorID ~= MyOwner and actorID ~= MyID then -- 主人または自分ではない resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --友達のキャラクターを返す function GetFriends(argActors) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if IsFriend(actorID) == 1 then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --友達以外のキャラクターを返す function GetNotFriend(argActors) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if IsFriend(actorID) == 0 then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID else FriendExists = true -- 友達が居る --TraceAI("** Friend Exists ! : "..actorID) end end if TableSize(argActors) == resultActorsIndex then FriendExists = false end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --範囲内の相手を返す function GetInRangeActors(argActors, argID, argRange) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if GetDistance2(actorID, argID) <= argRange then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --範囲外の相手を返す function GetOutRangeActors(argActors, argID, argRange) local resultActors = {} local resultActorsIndex = 0 for argActorsIndex, actorID in ipairs(argActors) do if GetDistance2(actorID, argID) >= argRange then resultActorsIndex = resultActorsIndex + 1 resultActors[resultActorsIndex] = actorID end end return resultActors end ------------------------------------------------------------------------------------------------------------------------------------------- --テーブルから指定したIDと最短距離の相手を返す function GetNearActorID(argID, argActors) local resultID = 0 --戻り値 local min_dis = 100 --デフォルト比較距離 local dis = 100 --対象との距離 for argActorsIndex, actorID in ipairs(argActors) do dis = GetDistance2(argID,actorID) if dis ~= -1 and dis < min_dis then resultID = actorID min_dis = dis end end return resultID end ------------------------------------------------------------------------------------------------------------------------------------------- --主人が攻撃中の相手を返す function GetOwnerAttackEnemyID(dist) local target = 0 local owner = MyOwner if GetV(V_MOTION,owner) == MOTION_ATTACK or GetV(V_MOTION,owner) == MOTION_CASTING or GetV(V_MOTION,owner) == MOTION_SKILL then local o_target = GetV(V_TARGET, owner) -- 主人のターゲット取得 if IsMonster(o_target) == 1 and IsMob(M_ACT,o_target) ~= -2 then -- 攻撃、詠唱、スキル攻撃した相手がMobかつ攻撃可能対象 target = o_target end end if GetV(V_MOTION,owner) == MOTION_CASTING then -- 詠唱中は if GetDistance2(owner,target) > dist then -- ターゲットとの距離が遠かったらキャンセル target = 0 end else if GetDistance2(owner,target) > 4 then -- ターゲットが近接射程外ならキャンセル target = 0 end end return target end ------------------------------------------------------------------------------------------------------------------------------------------- --友達の誰かが攻撃中の相手を返す function GetFriendAttackEnemyID(dist) local target = 0 for friend, job in pairs(FriendList) do if GetV(V_MOTION,friend) == MOTION_ATTACK or GetV(V_MOTION,friend) == MOTION_CASTING or GetV(V_MOTION,friend) == MOTION_SKILL then if IsFriend(friend) == 1 then local f_target = GetV(V_TARGET, friend) if IsMonster(f_target) == 1 and IsMob(M_ACT,f_target) ~= -2 then -- 攻撃、詠唱、スキル攻撃した相手がMobなら target = f_target if GetDistance2(friend,target) > dist then -- ターゲットとの距離が遠かったらキャンセル target = 0 end return target end end end end return target end ------------------------------------------------------------------------------------------------------------------------------------------- --友達の誰かを攻撃中の相手を返す function GetFriendEnemyID(dist) local actors = {} local resultActors = {} local resultIndex = 0 local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = GetEnemys(actors) --↑のうち攻撃可能 actors = GetNotMotionActors(actors, {MOTION_DEAD}) --↑のうち死にモーション以外 actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 for index, actorID in ipairs(actors) do if IsFriend(GetV(V_TARGET,actorID)) == 1 then -- 敵のターゲットが友達 resultIndex = resultIndex+1 resultActors[resultIndex] = actorID end end resultEnemyID = GetNearActorID(MyID, resultActors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --主人を攻撃中の中から一番近い敵を返す function GetOwnerEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = GetTargetActors(actors, MyOwner) --↑のうちターゲットが主人 actors = GetEnemys(actors) --↑のうち攻撃可能 actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = GetNotMotionActors(actors, {MOTION_DEAD}) --↑のうち死にモーション以外 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --自分を攻撃中の中から一番近い敵を探す function GetMyEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = GetTargetActors(actors, MyID) --↑のうちターゲットが自分 actors = GetEnemys(actors) --↑のうち攻撃可能 actors = GetNotMotionActors(actors, {MOTION_DEAD}) --↑のうち死にモーション以外 actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --誰もタゲって無くて主人から指定セル(dist)以内にいる一番近い敵 function GetFreeEnemyID(dist,rank) local actors = {} local prior = {} local resultEnemyID = 0 OnActive = true -- 索敵開始 actors = GetActors() --画面内の全員 actors = GetTargetActors(actors, 0) --↑のうちターゲット0 if HalfActive and ActiveFlag==false then -- ノンアクで先制設定があるとき actors = GetRaidEnemys(actors) -- ↑のうち先制リストのMob if TableSize(actors) < 1 then -- リストに居なかったら OnActive = false -- アクティブ索敵終了 return 0 -- 0を返して抜ける end else -- そうでなければ actors = GetEnemys(actors) --↑のうち攻撃可能 end actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = GetNotMotionActors(actors, {MOTION_DEAD, MOTION_ATTACK, MOTION_ATTACK2}) --↑のうち死&攻撃モーション以外 prior = GetEnemyByRecord(actors,3) --↑のうち優先度が高い敵 if not prior[1] then actors = GetEnemyByRecord(actors,rank) --↑のうち一番弱い else actors = prior end resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い -- 他人とターゲットの距離チェック if resultEnemyID ~= 0 then if not IsNotNoManner(MyID,resultEnemyID) then resultEnemyID = 0 end end OnActive = false -- アクティブ索敵終了 return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --主人を遠距離攻撃中の中から一番近い敵を探す function GetOwnerOutRangeEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = GetTargetActors(actors, MyOwner) --↑のうちターゲットが主人 actors = GetEnemys(actors) --↑のうち攻撃可能 actors = GetOutRangeActors(actors, MyOwner, 4) --↑のうちオーナーから4セル以上 actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = GetNotMotionActors(actors, {MOTION_DEAD}) --↑のうち死にモーション以外 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --自分を遠距離攻撃中の中から一番近い敵を探す function GetOutRangeEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = GetTargetActors(actors, MyID) --↑のうちターゲットが自分 actors = GetEnemys(actors) --↑のうち攻撃可能 actors = GetOutRangeActors(actors, MyOwner, 4) --↑のうち自分から4セル以上 actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = GetNotMotionActors(actors, {MOTION_DEAD}) --↑のうち死にモーション以外 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------- -- 指定セル内の友達ではないプレイヤーキャラクターを返す function GetPlayersNearID(dist) local actors = {} local resultPlayerID = 0 actors = GetActors() -- 画面内の全員 actors = GetPlayers(actors) -- ↑のうち主人以外のPC actors = GetInRangeActors(actors, MyOwner, dist) --↑のうち自分からdistセル以内 actors = GetNotFriend(actors) -- ↑のうち友達ではないPC actors = GetNotMotionActors(actors, {MOTION_DEAD}) --↑のうち死にモーション以外 resultPlayerID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultPlayerID end ------------------------------------------------------------------------------------------------------------------------------------------- -- 友達ではなく、同じターゲットを持つ一番近いプレイヤーキャラクターを返す function GetNeighborID() local actors = {} local resultPlayerID = 0 actors = GetActors() -- 画面内の全員 actors = GetPlayers(actors) -- ↑のうち主人以外のPC actors = GetNotFriend(actors) -- ↑のうち友達ではないPC actors = GetTargetActors(actors, GetV(V_TARGET,MyID)) -- ↑のうち自分と同じタゲ resultPlayerID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultPlayerID end ------------------------------------------------------------------------------------------------------------------------------------------- -- 平均戦闘時間の数値をもとに,最も弱い敵又は最も強い敵を返す function GetEnemyByRecord(actors,case) local resultActors = {} local resultIndex = 0 local resultID = 0 local value = 0 local otherMobs = 0 -- 基準決定 if case == nil or case == 0 then -- caseが0か未設定なら return actors -- actorsをそのまま返して抜ける elseif case == 1 then -- caseが1なら value = 0 -- 記録が0秒に近い場合(弱い相手を探す) elseif case == 2 then -- caseが2なら value = 60000 -- 記録が60秒に近い場合(強い相手を探す) elseif case == 3 then -- caseが3なら value = 21 -- 優先度が高い相手を探す(優先度最大20) end local min_value = 60000 -- デフォルト比較基準 for i,actorID in ipairs(actors)do local record = 0 local id = GetV(V_HOMUNTYPE,actorID) if case == 1 or case == 2 then if IsMob(M_RECORD,actorID) == 1 then -- 記録があれば record = Mob[id][M_RECORD] -- 記録読み込み end elseif case == 3 then if IsMob(M_RANK,actorID) == 1 then -- ランクがあれば record = MathThreshold(Mob[id][M_RANK],20) -- 記録読み込み end end if record ~= 0 then -- 記録が空または0じゃなければ local sub = MathABS(value - record) if sub < min_value then min_value = sub resultID = id -- 該当するMob種ID end end end if resultID ~= 0 and resultID == GetV(V_HOMUNTYPE,MyOwner) then -- 主人が最弱or最強Mobを叩いていたら return {GetV(V_TARGET,MyOwner)} -- ターゲットはそれにする end if resultID ~= 0 then -- 一番弱いor強い相手が見つかれば for i,actorID in ipairs(actors)do -- もう一度その種類のMobを探しなおして if GetV(V_HOMUNTYPE,actorID) == resultID then resultIndex = resultIndex +1 resultActors[resultIndex] = actorID else otherMobs = otherMobs + 1 -- それ以外のMobカウント end end return resultActors -- 配列にして返す else if case == 3 then -- case3の時は空行列を返す return {} else return actors -- データが一切なければそのまま返す end end end ------------------------------------------------------------------------------------------------------------------------------------------- function GetRankedEnemyID(dist,case) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = GetOurEnemys(actors) --↑のうちターゲットが自分orホムor友達 actors = GetEnemys(actors) --↑のうち攻撃可能 actors = GetInRangeActors(actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = GetEnemyByRecord(actors,case) --↑のうち、一番強いor弱いor最優先 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------- -- 逃避方向を計算する(主人からenemyに対して反対方向へdistだけ移動) ------------------------------------------- function MoveBackward (enemy,dist) -- ホムを主人の後方に移動させる if enemy ~= 0 and IsMonster(enemy) == 1 and IsInSight(enemy) == 1 then -- ターゲットが存在し,かつ画面内 local ex, ey = GetV(V_POSITION,enemy) -- 敵の座標 local ox, oy = GetV(V_POSITION,MyOwner) -- 主人の座標 local mx, my = GetV(V_POSITION,MyID) -- 自分の座標 local dirX, dirY = 0, 0 dirX, dirY = VectorCheck(ex,ey,ox,oy) if EscapeDirection ~= 0 and -9 < EscapeDirection and EscapeDirection < 9 then dirX, dirY = RotateDirection(dirX, dirY, EscapeDirection) end local x, y = ox + dist*dirX, oy + dist*dirY if GetDistance2(MyOwner,enemy) > 0 then -- 敵と主人が同セル以外で if mx ~= x or my ~= y then -- 現在地と違えば Move(MyID,x,y) end end else OnFOLLOW_ST() -- 敵が居ない時はFOLLOW_STと同じ動作 end end ------------------------------------------- -- x1, y1を基点とし,2点間のベクトル計算する ------------------------------------------- function VectorCheck(x1,y1,x2,y2) local vx,vy = 0,0 if x1 - x2 > 0 then vx = -1 elseif x1 - x2 < 0 then vx = 1 end if y1 - y2 > 0 then vy = -1 elseif y1 - y2 < 0 then vy = 1 end return vx, vy end ------------------------------------------- -- 方向転換 --812 --7*3 --654 ------------------------------------------- function RotateDirection(x,y,rotate) local tableX = {0,1,1,1,0,-1,-1,-1} local tableY = {1,1,0,-1,-1,-1,0,1} local index = 0 for i,vx in ipairs(tableX) do if x == tableX[i] and y == tableY[i] then index = i break end end index = index + rotate if index > 8 then index = index - 8 elseif index < 1 then index = index + 8 end return tableX[index], tableY[index] end ------------------------------------------- -- 逃避行動状態に関する定義 ------------------------------------------- function OnESCAPE_ST () --TraceAI("ESCAPE_ST ") PrintMessage(MyState,0) if EscapePattern == 0 then local enemy = GetV(V_TARGET,MyOwner) MoveBackward (enemy,EscapeRange) else target = GetMyEnemyID(OnBattleDistance) -- 自分を攻撃する敵を探す if target ~= 0 then local dist = math.max(OnBattleDistance,EscapeRange) -- 距離の広い方 if GetDistance2(MyID,target) < 3 and GetTick() > MoveDelay then -- 接敵されたら逃げる SetRondo(EscapePattern,EscapeRange) Rondo(EscapePattern,EscapeRange) elseif GetDistance2(MyID,MyOwner) > dist then -- 離れすぎたら主人の近くへ MoveToNear (MyOwner, ChaseDistance) end else OnFOLLOW_ST() -- 敵が居ない時はFOLLOW_STと同じ動作 end end -- 回復したら通常行動に戻る if GetPerHP(MyID) >= HomunculusSafetyHP then NowRondo = false if LastState == FOLLOW_CMD_ST then -- 休息状態から入った場合は MyState = FOLLOW_CMD_ST -- 休息状態に回帰 LastState = ESCAPE_ST --TraceAI("ESCAPE exit to FOLLOW !!! ") return else --TraceAI("ESCAPE exit to IDLE !!! ") MyState = IDLE_ST end end end ------------------------------------------------------- -- 主人の移動履歴を記録するための関数 -- x, y : 主人の現在地 ------------------------------------------------------- function SaveLastWalk (x,y) -- 主人の足跡を記録 if x ~= OwnerX or y ~= OwnerY then PrevPos3x = PrevPos2x PrevPos3y = PrevPos2y PrevPos2x = PrevPos1x PrevPos2y = PrevPos1y PrevPos1x = OwnerX PrevPos1y = OwnerY OwnerX = x OwnerY = y end --TraceAI("x1, y1 = "..PrevPos1x..PrevPos1y.." x2, y2 = "..PrevPos2x..PrevPos2y) end function ResetWalk() OwnerX, OwnerY = GetV(V_POSITION,MyOwner) PrevPos3x = OwnerX PrevPos3y = OwnerY PrevPos2x = OwnerX PrevPos2y = OwnerY PrevPos1x = OwnerX PrevPos1y = OwnerY end ------------------------------------------- -- 先行行動状態に関する定義 ------------------------------------------- function OnFORWARD_ST() local distance = GetDistance2(MyID,MyOwner) local motion = GetV(V_MOTION,MyOwner) if motion ~= MOTION_MOVE then -- 移動モーションじゃなくなった時 MyState = IDLE_ST -- 待機状態に戻る StayFlag = true else --TraceAI("OnFORWARD_ST") -- リーフの場合自動緊急回避 local pass = os.difftime(os.time(),BufferTime) if EmergencyAvoid and BufferDelay < pass and AvoidDelay < pass and RecoverDelay < GetTick() and GetV(V_MOTION,MyOwner) == MOTION_MOVE then if CheckMyType(MyType) == LIF then -- リーフなら if AutoSkillSPLimit < GetPerSP(MyID) then AutoSkill_SELF(2) end end end local x, y = GetV(V_POSITION,MyID) local v1x, v1y = 0,0 local v2x, v2y = 0,0 local v3x, v3y = 0,0 local dirX, dirY = 0,0 v1x, v1y = VectorCheck(PrevPos1x,PrevPos1y,OwnerX,OwnerY) -- 現在地と前回の位置ベクトル v2x, v2y = VectorCheck(PrevPos2x,PrevPos2y,PrevPos1x,PrevPos1y) -- 前回の位置と前々回の位置のベクトル v3x, v3y = VectorCheck(PrevPos3x,PrevPos3y,PrevPos1x,PrevPos1y) -- 前回の位置と前々回の位置のベクトル --TraceAI("v1x , v1y = "..v1x..v1y.." v2x , v2y = "..v2x..v2y) dirX = (v1x + v2x + v3x) -- ベクトルの正規化 dirY = (v1y + v2y + v3y) local ax = MathABS(dirX) local ay = MathABS(dirY) local div = math.max(ax,ay) -- 大きいほう dirX = dirX/div dirY = dirY/div --TraceAI("dirX , dirY = "..dirX..dirY) if dirX == 0 and dirY == 0 then -- 目的ベクトルが0,0(主人の座標)のときは return -- なにもしないで抜ける end dirX = OwnerX + math.ceil(dirX * ForwardDistance) dirY = OwnerY + math.ceil(dirY * ForwardDistance) if x ~= dirX or y ~= dirY then -- 現在地と目的地が違えば移動 Move(MyID,dirX,dirY) end PrintMessage(MyState,0) end end ------------------------------------------------------ -- 後衛状態の関数 -- 敵から退避しつつスキル攻撃 ------------------------------------------------------ function OnSHOOTING_ST() --TraceAI ("OnSHOOTING_ST") local HomSP = GetV (V_SP,MyID) local target LimitSP = 30 if HomSP < LimitSP then -- SPが限界以下になったら状態解除 ResetEnemy() MyState = IDLE_ST return end if GetDistance2(MyID,MyOwner) > OnBattleDistance then -- 離れすぎると状態解除 ResetEnemy() MyState = FOLLOW_ST return end if MyEnemy == 0 then MyEnemy = GetEnemy() -- 索敵 end if MyEnemy ~= 0 then local motion = GetV(V_MOTION, MyEnemy) local i_motion = GetV(V_MOTION, 100000 - MyEnemy) if motion == MOTION_DEAD or motion == -1 or i_motion == MOTION_DEAD then -- 攻撃対象が死んだとき LostEnemy = MyEnemy ResetEnemy() -- 敵情報をリセット return end if GetTick() > AttackSkillDelay then -- ディレイ間隔2秒経過していたら MySkillLevel,MySkill = SetActiveSkill(ShootLevel) if MySkillLevel <= 0 then MySkillLevel = 1 end SkillObject (MyID,MySkillLevel,MySkill,MyEnemy) -- スキル発動 AttackSkillDelay = GetTick() + SkillDelay end target = GetMyEnemyID(OnBattleDistance) -- 自分を攻撃する敵を探す if target ~= 0 then if GetDistance2(MyID,target) < 3 and GetTick() > MoveDelay then -- 接敵されたら1秒間隔で逃げる SetRondo(PatrolPattern,PatrolRange) Rondo(PatrolPattern,PatrolRange) end end PrintMessage(MyState,MyEnemy) end end ---------------------------------------------------------- -- オーナーチェンジする関数 ---------------------------------------------------------- function ChangeOwner(id) local value = 0 if id then -- idの引数がある場合 if MyOwner ~= GetV(V_OWNER,MyID) then MyOwner = GetV(V_OWNER,MyID) elseif MyOwner == GetV(V_OWNER,MyID) and (IsPlayer(id) == 1 or IsHomunculus(id) == 1) and IsFriend(id) == 1 then MyOwner = id value = 1 end else if MyOwner == GetV(V_OWNER,MyID) then -- 引数が無い場合は一番近い友達 local actors = GetActors() actors = GetFriends(actors) local owner = GetNearActorID(MyID, actors) if owner ~= 0 then MyOwner = owner value = 1 end else MyOwner = GetV(V_OWNER,MyID) -- 2回目で元の主人に戻る end end PrintMessage(TRIGGER_ST,MyOwner,TRIGGER_CHANGE_OWNER,value,2,1) MyState = ALERT_ST -- 主人の周りを旋回 end ---------------------------------------------------------- -- 指定座標上に居るキャラクターのIDを返す ---------------------------------------------------------- function GetID(dx,dy) local actors = GetActors() -- 画面内のオブジェクト local id = 0 -- ID保存用 for i,v in ipairs (actors) do local x,y = GetV(V_POSITION,v) -- 座標取得 if (x == dx and y == dy) then -- クリック地点の対象なら id = v -- そのIDを保存 break end end if id ~= 0 then local ty = GetV(V_HOMUNTYPE,id) TraceAI(" ** GetID : " ..id.." --Type : "..ty) end return id end ---------------------------------------------------------- -- 予約コマンドを利用した登録の関数 ---------------------------------------------------------- function ReservedCommand(msg) local id local dx,dy if msg[1] == MOVE_CMD then -- 移動コマンドの場合は友達登録動作 dx,dy = msg[2],msg[3] -- 指定先の座標を取得 id = GetID(dx,dy) -- 指定座標上の対象ID取得 if id == GetV(V_OWNER,MyID) then -- 主人のセルクリック EditControl(ALT_SHIFT_OWNER_CLICK,id) elseif id == MyID then -- 自分のセルクリック EditControl(ALT_SHIFT_MY_CLICK,id) else -- 自分以外の誰か EditControl(ALT_SHIFT_CELL_CLICK,id) -- 友達リストに追加 end elseif msg[1] == ATTACK_OBJECT_CMD then -- 攻撃命令コマンドの場合は無視する敵登録動作 id = msg[2] -- 攻撃対象のID取得 dx,dy = GetV(V_POSITION,id) -- 対象の座標取得 if MyState == FOLLOW_CMD_ST then -- Alt+Tによる休息状態なら EditControl(ALT_T_SHIFT_CLICK,id) -- 無視リストに追加 else -- それ以外なら if GetV(V_MOTION,GetV(V_OWNER,MyID)) == MOTION_SIT then -- 主人が座ってる時に EditControl(ALT_SHIFT_CLICK_SIT,id) -- 特殊強化対象指定 else EditControl(ALT_SHIFT_CLICK,id) -- 先制リストに追加 end end end end ---------------------------------------------------------- -- 条件発生系コントロール関数 -- switch:トリガータイプ argTrigger:行動内容 id:対象ID x,y:座標 count:条件数値 ---------------------------------------------------------- function TriggerControl(switch,argTrigger,id,x,y,count) if switch == TRIGGER_ON then for i,trigger in ipairs(argTrigger) do if trigger == TRIGGER_OnESCAPE then if MyState~=MOVE_CMD_ST then if MyState==FOLLOW_CMD_ST then LastState = MyState end --TraceAI(" last state : "..LastState) ResetEnemy() MyState = ESCAPE_ST T_FLAG[TRIGGER_OnESCAPE] = true end elseif trigger == TRIGGER_OnSEARCH and not T_FLAG[TRIGGER_OnSEARCH] then MyState = SEARCH_ST T_FLAG[TRIGGER_OnSEARCH] = true elseif trigger == TRIGGER_OnFORWARD and not T_FLAG[TRIGGER_OnFORWARD] then OnForward = true T_FLAG[TRIGGER_OnFORWARD] = true elseif trigger == TRIGGER_OnSHOOTING and not T_FLAG[TRIGGER_OnSHOOTING] then ShootingMode = true T_FLAG[TRIGGER_OnSHOOTING] = true elseif trigger == TRIGGER_ACTIVE and not T_FLAG[TRIGGER_ACTIVE] then ActiveFlag = true T_FLAG[TRIGGER_ACTIVE] = true elseif trigger == TRIGGER_CHANGE_OWNER and not T_FLAG[TRIGGER_CHANGE_OWNER] then ChangeOwner() T_FLAG[TRIGGER_CHANGE_OWNER] = true elseif trigger == TRIGGER_ASP_INC and not T_FLAG[TRIGGER_ASP_INC] then AutoSkill = AutoSkill + ASP_Increase ASP_Neutral = ASP_Neutral + ASP_Increase TraceAI("ASP Inc :"..AutoSkill) T_FLAG[TRIGGER_ASP_INC] = true elseif trigger == TRIGGER_ASL_INC and not T_FLAG[TRIGGER_ASL_INC] then AdditionalLevel = AdditionalLevel + ASL_Increase T_FLAG[TRIGGER_ASL_INC] = true elseif trigger == TRIGGER_OnMOVE and not T_FLAG[TRIGGER_OnMOVE] then ReturnFlag = true local x,y = GetV(V_POSITION,MyID) if MyState ~= MOVE_CMD_ST then LastState = MyState end MyState = MOVE_CMD_ST local x,y = GetV(V_POSITION,MyID) MyDestX = T_MoveDirection[1] + x MyDestY = T_MoveDirection[2] + y Move(MyID,MyDestX,MyDestY) T_FLAG[TRIGGER_OnMOVE] = true end end elseif switch == TRIGGER_OFF then -- トリガーオフ for i,trigger in ipairs(argTrigger) do if trigger == TRIGGER_OnESCAPE and T_FLAG[TRIGGER_OnESCAPE] then if LastState==FOLLOW_CMD_ST then MyState = LastState LastState = ESCAPE_ST end T_FLAG[TRIGGER_OnESCAPE] = false TraceAI("exit escape ...") elseif trigger == TRIGGER_OnSEARCH and T_FLAG[TRIGGER_OnSEARCH] then T_FLAG[TRIGGER_OnSEARCH] = false elseif trigger == TRIGGER_OnFORWARD and T_FLAG[TRIGGER_OnFORWARD] then T_FLAG[TRIGGER_OnFORWARD] = false OnForward = false elseif trigger == TRIGGER_OnSHOOTING and T_FLAG[TRIGGER_OnSHOOTING] then ShootingMode = false T_FLAG[TRIGGER_OnSHOOTING] = false elseif trigger == TRIGGER_ACTIVE and T_FLAG[TRIGGER_ACTIVE] then ActiveFlag = false T_FLAG[TRIGGER_ACTIVE] = false elseif trigger == TRIGGER_CHANGE_OWNER and T_FLAG[TRIGGER_CHANGE_OWNER] then MyOwner = GetV(V_OWNER,MyID) T_FLAG[TRIGGER_CHANGE_OWNER] = false elseif trigger == TRIGGER_ASP_INC and T_FLAG[TRIGGER_ASP_INC] then ASP_Neutral = ASP_Neutral - ASP_Increase TraceAI("ASP Dec :"..AutoSkill) T_FLAG[TRIGGER_ASP_INC] = false elseif trigger == TRIGGER_ASL_INC and T_FLAG[TRIGGER_ASL_INC] then AdditionalLevel = 0 --TraceAI("AdditionalLevel :"..AdditionalLevel) T_FLAG[TRIGGER_ASL_INC] = false elseif trigger == TRIGGER_OnMOVE and T_FLAG[TRIGGER_OnMOVE] then T_FLAG[TRIGGER_OnMOVE] = false end end elseif switch == TRIGGER_PING then for i,trigger in ipairs(argTrigger) do if trigger == TRIGGER_OnESCAPE then LastState = MyState MyState = ESCAPE_ST elseif trigger == TRIGGER_OnSEARCH then MyState = SEARCH_ST elseif trigger == TRIGGER_OnFORWARD then if OnForward then OnForward = false else OnForward = true end elseif trigger == TRIGGER_OnSHOOTING then MyState = SHOOTING_ST elseif trigger == TRIGGER_ACTIVE then ActiveFlag = true elseif trigger == TRIGGER_CHANGE_OWNER then ChangeOwner(id) elseif trigger == TRIGGER_ASP_INC then AutoSkill = AutoSkill + ASP_Increase TraceAI("ASP Inc :"..AutoSkill) elseif trigger == TRIGGER_ASL_INC then AdditionalLevel = AdditionalLevel + ASL_Increase TraceAI("AdditionalLevel :"..AdditionalLevel) elseif trigger == TRIGGER_OnMOVE then ReturnFlag = true if MyState ~= MOVE_CMD_ST then LastState = MyState end MyState = MOVE_CMD_ST local x,y = GetV(V_POSITION,MyID) MyDestX = T_MoveDirection[1] + x MyDestY = T_MoveDirection[2] + y Move(MyID,MyDestX,MyDestY) elseif trigger == TRIGGER_CHANGE_MODE then ShiftMode(ModeList) end end end end ---------------------------------------------------------- -- 登録操作系コントロール関数 -- edit:編集内容 id:登録対象 ---------------------------------------------------------- function EditControl(edit,actor,inc) local id = GetV(V_HOMUNTYPE,actor) local dammy = IsMob(100,actor) if edit == EDIT_FRIEND then if IsPlayer(actor) == 1 and actor ~= GetV(V_OWNER,MyID) then -- IDが主人以外のプレイヤー AddFriend(actor) -- 友達リストに追加 elseif IsHomunculus(actor) == 1 and actor ~= MyID then -- IDが自分以外のホムンクルス AddFriend(actor) -- 友達リストに追加 end elseif edit == EDIT_SKILL then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then AddNoSkill(actor) end elseif edit == EDIT_SKILL_LEVEL then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then if inc then ControlSkillLevel(actor,inc) else ControlSkillLevel(actor,0) end end elseif edit == EDIT_IGNORE then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then AddIgnore(actor) end elseif edit == EDIT_RAID then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then AddRaid(actor) end elseif edit == EDIT_BOOST then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then AddBoost(actor) end elseif edit == EDIT_CAST then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then AddCast(actor) end elseif edit == EDIT_RANK then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then local rank if IsMob(M_RANK,actor) == 1 then rank = Mob[id][M_RANK] else rank = 0 end if rank then if rank < 20 then -- 優先度が最大未満なら Mob[id][M_RANK] = rank + 1 -- 優先度+1 elseif rank > 20 then Mob[id][M_RANK] = rank - 20 end end SaveRecord(MobFilename) end elseif edit == EDIT_BONUS then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then local rank = Mob[id][M_RANK] -- 対象の優先度記録 if rank then if rank > 0 then if rank <= 20 then Mob[id][M_RANK] = 20 + rank -- 優先度反転 else Mob[id][M_RANK] = rank - 20 -- 優先度反転 end else Mob[id][M_RANK] = 21 end end SaveRecord(MobFilename) PrintMessage(EDIT_ST,actor,EDIT_BONUS,Mob[id][M_RANK],2,1) end elseif edit == EDIT_FULL_SKILL then if IsMonster(actor) == 1 and IsPlayer(actor) == 0 and IsHomunculus(actor) == 0 then if inc then ControlSkillLevel(actor,inc+6) else ControlSkillLevel(actor,6) end end elseif edit == EDIT_FRIEND_ALL then AddFriendAll() elseif edit == EDIT_FRIEND_DEL then ClearFriendList() else return end end ------------------------------------------------------------------------- -- 友達追加・削除 ------------------------------------------------------------------------- function AddFriend (id) if IsFriend(id) == 0 then -- idが友達じゃなかったら追加 if IsPlayer(id) == 1 then -- プレイヤーなら FriendList[id] = GetV(V_HOMUNTYPE,id) SaveFriend("Friend",FriendList) -- 保存 elseif IsHomunculus(id) == 1 then -- ホムなら HomFriendList[id] = GetV(V_HOMUNTYPE,id) -- ホム友達リストに追加 end Neighbor = 0 FriendIndex = TableSize(FriendList) + TableSize(HomFriendList) TraceAI(" ** Add Friend! : " ..id.." *Job : "..GetV(V_HOMUNTYPE,id).." --No."..FriendIndex) if MyState ~= ATTACK_ST then -- 戦闘中の自動追加だったらレスは無し Response(1) -- 追加レスポンス end PrintMessage(EDIT_ST,id,EDIT_FRIEND,1,2,1) else -- 既に友達だったら削除 if IsPlayer(id) == 1 then FriendList[id] = nil SaveFriend("Friend",FriendList) -- 保存 elseif IsHomunculus(id) == 1 then HomFriendList[id] = nil -- ホム友達リストから削除 end FriendIndex = TableSize(FriendList) + TableSize(HomFriendList) TraceAI(" ** Delete Friend! : " ..id.." LestMem : "..FriendIndex) Response(0) -- 削除レスポンス PrintMessage(EDIT_ST,id,EDIT_FRIEND,0,2,1) end end ------------------------------------------------------------------------- -- 自動友達登録 ------------------------------------------------------------------------- function AutoAddFriend() if AutoFriend then -- 自動友達登録するなら if NeighborFlag and Neighbor == 0 then -- 近くにプレイヤーが存在するとき(攻撃の度にactor回すのが嫌なのでIDLEでフラグ立て Neighbor = GetNeighborID() -- タゲの被ってる人が居たら友達の可能性 --TraceAI("** Neighbor : "..Neighbor) end if Neighbor ~= 0 then if GetV(V_TARGET,Neighbor) == GetV(V_TARGET,MyOwner) then -- 友達かもしれない人のターゲットが主人と同じとき local n_motion = GetV(V_MOTION,Neighbor) local o_motion = GetV(V_MOTION,MyOwner) if (n_motion == MOTION_ATTACK or n_motion == MOTION_ATTACK2 or n_motion == MOTION_SKILL or n_motion == MOTION_CASTING) then if (o_motion == MOTION_ATTACK or o_motion == MOTION_ATTACK2 or o_motion == MOTION_SKILL or o_motion == MOTION_CASTING) then SameTargetCount = SameTargetCount + 1 end end --TraceAI("** count : "..SameTargetCount) else -- そうじゃなかったら Neighbor = 0 -- 勘違いだったってことで SameTargetCount = 0 end if SameTargetCount >= TamedDegree then -- 20回くらい被ってたら SameTargetCount = 0 AddFriend(Neighbor) -- 友達と認識 end end end end ------------------------------------------------------------------------- -- 画面内全員友達登録 ------------------------------------------------------------------------- function AddFriendAll() local actors = GetActors() actors = GetPlayers(actors) -- プレイヤーまたはホム actors = GetNotFriend(actors) -- 友達ではない for i,actorID in ipairs(actors) do if IsPlayer(actorID) == 1 then FriendList[actorID] = GetV(V_HOMUNTYPE,actorID) elseif IsHomunculus(actorID) == 1 then HomFriendList[actorID] = GetV(V_HOMUNTYPE,actorID) end end FriendIndex = TableSize(FriendList) + TableSize(HomFriendList) TraceAI(" ------------ add all friend !! number : "..FriendIndex) Response(2) SaveFriend("Friend",FriendList) PrintMessage(EDIT_ST,0,EDIT_FRIEND_ALL,0,2,1) end ------------------------------------------------------------------------- -- 友達登録クリア ------------------------------------------------------------------------- function ClearFriendList() for i, v in pairs(FriendList) do FriendList[i] = nil end for i, v in pairs(HomFriendList) do HomFriendList[i] = nil end FriendIndex = TableSize(FriendList) + TableSize(HomFriendList) TraceAI(" ////// clear all friend ... ") Response(3) SaveFriend("Friend",FriendList) PrintMessage(EDIT_ST,0,EDIT_FRIEND_DEL,0,2,1) end ------------------------------------------------------------------------- -- 無視する敵追加・削除 ------------------------------------------------------------------------- function AddIgnore (id) local index = GetV(V_HOMUNTYPE,id) -- 種族IDを取得 if IsMob(M_ACT,id) >= 0 then -- idが既に登録されてなかったら追加 Mob[index][M_ACT] = ACT_IGNORE Mob[index][M_RANK] = 0 -- 優先度設定を初期化 TraceAI(" // Ignore Mob! : " ..index) Response(1) elseif IsMob(M_ACT,id) == -1 then -- 既に無視登録されてたら Mob[index][M_ACT] = ACT_NO -- 完全無視敵 TraceAI(" // NoRegard Mob! : " ..index) Response(2) elseif IsMob(M_ACT,id) == -2 then -- 既に完全無視登録されてたら Mob[index][M_ACT] = ACT_ESCAPE -- 退避対象 TraceAI(" // Escape Mob! : " ..index) Response(3) else -- それ以外は通常判定に Mob[index][M_ACT] = ACT_NORMAL TraceAI(" // Normal Mob! : " ..index) Response(0) end SaveRecord(MobFilename) PrintMessage(EDIT_ST,id,EDIT_IGNORE,Mob[index][M_ACT],2,1) end ------------------------------------------------------------------------- -- 先制する敵追加・削除 ------------------------------------------------------------------------- function AddRaid (id) local index = GetV(V_HOMUNTYPE,id) -- 種族IDを取得 if IsMob(M_ACT,id) ~= 2 then -- idが既に登録されてなかったら追加 Mob[index][M_ACT] = ACT_RAID TraceAI(" // Raid Mob! : " ..index) MyState = ALERT_ST else -- 既に登録されてたら削除 Mob[index][M_ACT] = ACT_NORMAL TraceAI(" // Normal Mob! : " ..index) Response(0) end SaveRecord(MobFilename) PrintMessage(EDIT_ST,id,EDIT_RAID,Mob[index][M_ACT],2,1) end ------------------------------------------------------------------------- -- スキルを使わない敵追加・削除 ------------------------------------------------------------------------- function AddNoSkill (id) local index = GetV(V_HOMUNTYPE,id) -- 種族IDを取得 if IsMob(M_SKILL,id) >= 0 then -- idが既に登録されてなかったら追加 Mob[index][M_SKILL] = SKILL_NO TraceAI(" // NoSkill! : "..index) Response(1) else -- 既に登録されてたら削除 Mob[index][M_SKILL] = SKILL_AUTO TraceAI(" // UseSkill! : " ..index) Response(0) end SaveRecord(MobFilename) PrintMessage(EDIT_ST,id,EDIT_SKILL,Mob[index][M_SKILL],2,1) end ------------------------------------------------------------------------- -- 詠唱妨害する敵追加・削除 ------------------------------------------------------------------------- function AddCast (id) local index = GetV(V_HOMUNTYPE,id) -- 種族IDを取得 if IsMob(M_CAST,id) == 0 then -- idが既に登録されてなかったら追加 Mob[index][M_CAST] = CAST_ON TraceAI(" // CastMob! : "..index) Response(1) else -- 既に登録されてたら削除 Mob[index][M_CAST] = CAST_OFF TraceAI(" // CastOff! : "..index) Response(0) end SaveRecord(MobFilename) PrintMessage(EDIT_ST,id,EDIT_CAST,Mob[index][M_CAST],2,1) end ------------------------------------------------------------------------- -- 特殊強化する敵追加・削除 -- リーフは例外的にレベル調整不要 ------------------------------------------------------------------------- function AddBoost (id) local index = GetV(V_HOMUNTYPE,id) -- 種族IDを取得 if Mob[index][M_BOOST] < 5 then -- 登録レベルが5未満なら if CheckMyType(MyType) == FILIR then -- フィーリルの場合 Mob[index][M_BOOST] = Mob[index][M_BOOST] + 1 -- 段階的に増加 else -- それ以外は Mob[index][M_BOOST] = 5 -- いきなり5で end TraceAI(" // BoostMob! : "..index.." Level : "..Mob[index][M_BOOST]) Response(2) else -- 既に登録されてたら削除 Mob[index][M_BOOST] = BOOST_OFF TraceAI(" // BoostOff! : "..index) Response(3) end SaveRecord(MobFilename) PrintMessage(EDIT_ST,id,EDIT_BOOST,Mob[index][M_BOOST],2,1) end ------------------------------------------------------------------------- -- スキルレベルを変更する関数 ------------------------------------------------------------------------- function ControlSkillLevel(id,level) local index = GetV(V_HOMUNTYPE,id) local inc = MathMod(level,6) -- レベル指定があるかどうか if inc ~= 0 then if Mob[index][M_SKILL] ~= level then Mob[index][M_SKILL] = level else Mob[index][M_SKILL] = SKILL_AUTO -- 同レベルの入力指定があった場合はデフォルトに end else if level < 6 then if Mob[index][M_SKILL] == SKILL_AUTO then -- デフォルト設定なら Mob[index][M_SKILL] = 1 else Mob[index][M_SKILL] = Mob[index][M_SKILL] + 1 -- レベル+1 end else if Mob[index][M_SKILL] == 11 then -- フルフラットLv5なら Mob[index][M_SKILL] = SKILL_AUTO -- デフォ設定に else Mob[index][M_SKILL] = inc + 7 end end end TraceAI(" -->> Static Skill Level : "..Mob[index][M_SKILL].." Enemy : "..index) SaveRecord(MobFilename) PrintMessage(EDIT_ST,id,EDIT_SKILL_LEVEL,Mob[index][M_SKILL],2,1) end ------------------------------------------------------------------------- -- アラートパフォーマンス ------------------------------------------------------------------------- function OnALERT_ST() --TraceAI("OnALERT_ST") if MoveDelay < GetTick() then if MC_AlertFlag then Rondo(MC_AlertPattern,2) else Rondo(9,1) -- 主人の周囲を旋回して通知 end end if not NowRondo then PatrolVector = 0 MyState = IDLE_ST return end end ------------------------------------------------------------------------- -- レスポンス -- Yes→主人の1マス左へ移動 No→主人の1マス右へ移動 ------------------------------------------------------------------------- function Response(ans) if ans == 1 then -- Yes Move(MyID,OwnerX-1,OwnerY) elseif ans == 0 then -- No Move(MyID,OwnerX+1,OwnerY) elseif ans == 2 then Move(MyID,OwnerX,OwnerY+1) -- up elseif ans == 3 then Move(MyID,OwnerX,OwnerY-1) -- down end end ------------------------------------------------------------------------- -- サーチング関数 ------------------------------------------------------------------------- function OnSEARCH_ST() TraceAI("OnSEARCH_ST") if SearchedObject == 0 then local objects = GetActors() -- 画面内オブジェクト取得 local distance = 12 for i,v in ipairs(objects) do local id = GetV(V_HOMUNTYPE,v) -- vの種ID取得 if id >= 1 and id <= 16 then -- ホムとPCの切り分け if v < 100000 then -- ホムだったら id = id + 6000 -- +6000 end end if v ~= MyID and v ~= GetV(V_OWNER,MyID) and IsSearch(TargetObjects,id) == 1 then if distance >= GetDistance2(GetV(V_OWNER,MyID),v) then SearchedObject = v distance = GetDistance2(GetV(V_OWNER,MyID),v) MyDestX,MyDestY = GetV(V_POSITION,SearchedObject) -- 対象の座標取得 end end end end if SearchedObject ~= 0 then PrintMessage(TRIGGER_ST,SearchedObject,TRIGGER_OnSEARCH,IsHomunculus(SearchedObject),2,1) if MoveDelay < GetTick() then Rondo(10,1) -- 目的地の周囲を旋回して通知 end if not NowRondo then MyDestX,MyDestY = 0,0 PatrolVector = 0 SearchedObject = 0 MyState = IDLE_ST return end else MyState = IDLE_ST end end ------------------------------------------------------------------------- -- 配列の中から該当の番号を探す ------------------------------------------------------------------------- function IsSearch(objects,id) for i,v in pairs(objects) do if v == 0 then return 0 elseif v == id then return 1 end end return 0 end function SkillObject(id,level,skill,obj) if skill > 8000 then SkillObjectL(id,level,skill,obj) PrintTalkMSG(MyID,skill,level,0) end end ------------------------------------------------------------------------- -- 寝落ち防止用 ------------------------------------------------------------------------- function ExitEvent() do local timestamp = os.time() local ownerMotion = GetV(V_MOTION,GetV(V_OWNER,MyID)) ExitEvent = function() local ownerMotion2 = GetV(V_MOTION,GetV(V_OWNER,MyID)) -- 主人のモーションが同じ時間 if ownerMotion ~= ownerMotion2 then ownerMotion = ownerMotion2 timestamp = os.time() ExitCheckTime = GetTick() return end if os.difftime( os.time(), timestamp ) >= ExitLimitTime*0.9 then -- 制限時間の9割超えたら MyState = ALERT_ST -- 常にアラート状態になる end if os.difftime( os.time(), timestamp ) >= ExitLimitTime then os.exit() -- 終了 end end end end ------------------------------------------------------------------------- -- SPR待ち用関数 ------------------------------------------------------------------------- function WaitUntilSPR() if GetPerSP(MyID) < SPR_Rate then local x,y = GetV(V_POSITION,MyID) if MyPosX ==x and MyPosY == y then local pass = os.difftime(os.time(),StayTime) if SPR_Wait <= pass and pass < 4 then SPR_Flag = true PreSP = GetV(V_SP,MyID) elseif pass == 4 then if GetV(V_SP,MyID) > PreSP then SPR_Flag = true StayTime = os.time() end else SPR_Flag = false if pass > 4 then StayTime = os.time() end end else SPR_Flag = false MyPosX = x MyPosY = y StayTime = os.time() end else SPR_Flag = false end end ------------------------------------------------------------------------- -- APSD補正関数 ------------------------------------------------------------------------- function AttackAccelerator(phase) local sx,sy = GetV(V_POSITION,MyID) local ex,ey = GetV(V_POSITION,MyEnemy) if AttackedFlag and not AdjustASPD and MultiAttack >= 2 then -- 攻撃フラグ立ってたら if MathMod(AttackStCycle,2) == 1 then Attack(MyID,MyEnemy) -- もう1回攻撃しとく if MoveDelay < GetTick() then if MovingCancel and MovingSP < GetPerSP(MyID) then HateOverlap = false AttackDistance = 1 local dirX,dirY = 0,0 local dirX,dirY = VectorCheck(sx,sy,ex,ey) if dirX == 0 and dirY == 0 then dirX = -1 end Move(MyID,sx+dirX,sy+dirY) Attack(MyID,MyEnemy) else Move(MyID,sx,sy) Attack(MyID,MyEnemy) -- もう1回攻撃しとく end end if MultiAttack == phase then AttackedFlag = false end else AttackedFlag = false end elseif MultiAttack == 1 then AttackedFlag = false end end ------------------------------------------------------------------------- -- MCh終了アラート用関数 ------------------------------------------------------------------------- function MC_AlertCheck() if MentalChange and MentalChangeAlert ~= 0 then -- メンタルチェンジ中でアラート指定あり local pass = os.difftime(os.time(),BufferTime) if BufferDelay > pass then if BufferDelay - pass <= MentalChangeAlert then -- 残り時間がアラート時間を下回ったら MyState = ALERT_ST -- アラート状態にして抜ける MC_AlertFlag = true return end else MentalChange = false MC_AlertFlag = false end end end ------------------------------------------------------------------------- -- スタックチェック用関数 ------------------------------------------------------------------------- function CheckStack() local mx, my = GetV(V_POSITION,MyID) -- 自分の位置 -- 移動のスタックチェック if MyState == FOLLOW_ST or MyState == CHASE_ST or MyState == MOVE_CMD_ST or MyState == FOLLOW_CMD_ST then local motion = GetV(V_MOTION,MyID) local case = 0 if motion == MOTION_MOVE or SPR_Flag then -- 移動モーションが確認できたら StandTime = GetTick() -- 時間更新 return else local pass = GetTick() - StandTime if pass >= 800 and pass < 1200 then -- 0.8秒以上1.2秒未満 case = 1 -- 解消ケース1 elseif pass >= 1200 then -- 1.5秒以上 case = 2 -- 解消ケース2 end if case == 0 then return elseif case == 1 then if MyState == FOLLOW_ST or MyState == FOLLOW_CMD_ST then local d = GetDistance2(MyID,MyOwner) if d > FollowDistance and not StayFlag then -- 主人から離れてるときに疑う Move(MyID,PrevPos1x,PrevPos1y) ReturnFlag = true TraceAI("-->> Stack on Follow") else StandTime = GetTick() end elseif MyState == CHASE_ST then local ex, ey = GetV(V_POSITION,MyEnemy) local dirX, dirY = VectorCheck(mx,my,ex,ey) -- 自分から敵への方向をチェック local rot = math.random(2) if rot == 2 then rot = -1 end dirX, dirY = RotateDirection(dirX,dirY,rot) -- ランダムでどっちかの方向にずらす Move(MyID,mx+dirX*2,my+dirY*2) ReturnFlag = true TraceAI("-->> Stack on Chase ?") elseif MyState == MOVE_CMD_ST then TraceAI("-->> Stack on Move ?") MyState = LastState StayFlag = false end elseif case == 2 then local d = GetDistance2(MyID,MyOwner) if MyState == FOLLOW_CMD_ST then if d <= FollowDistance then StandTime = GetTick() return end else MyState = IDLE_ST end if mx ~= PrevPos2x or my ~= PrevPos2y then Move(MyID,PrevPos2x,PrevPos2y) end StandTime = GetTick() LagCheckFlag = true -- ラグを疑う TraceAI("-->> Stack ?!") end end else StandTime = GetTick() -- それ以外の状態は無関係なので時間だけ更新 end -- 攻撃のスタックチェック if not OnlySkill and AttackFalseCheck then if MyState == ATTACK_ST and not NowAwaying then if MyEnemy ~= GetV(V_TARGET,MyID) then -- ターゲットした敵と実際のターゲットの違いを監視 AttackFalseCount = AttackFalseCount + 1 if AttackFalseCount > 3 then SkipStack = true TraceAI("-->> Stack on Attack !!") AttackFalseCount = 0 end else AttackFalseCount = 0 end local id = GetV(V_HOMUNTYPE,MyEnemy) -- MobID取得 local e_motion = GetV(V_MOTION,MyEnemy) local o_motion = GetV(V_MOTION,MyOwner) if not BonusFlag then -- 共闘ボーナス中ではなく if o_motion ~= MOTION_ATTACK or o_motion ~= MOTION_ATTACK2 then -- 主人が攻撃モーションじゃない時 if e_motion == MOTION_DAMAGE or IsPlant(MyEnemy) == 1 then -- 敵がダメージモーション或いは植物なら AttackFalseTime = GetTick() -- 時間更新 else local pass = GetTick() - AttackFalseTime if pass > 3000 then -- ダメージモーション以外のモーションが2.4秒以上確認されたら SkipStack = true AttackFalseTime = GetTick() -- 時間更新 TraceAI("-->> Stack on Attack ?") end end end end if SkipStack then local ex, ey = GetV(V_POSITION,MyEnemy) local dirX, dirY = VectorCheck(mx,my,ex,ey) -- 自分から敵への方向をチェック Move(MyID,mx+dirX,my+dirY) -- 敵の方向へ一歩進んでみる MoveDelay = GetTick() + 200 ReturnFlag = true SkipStack = false LagCheckFlag = true end else AttackFalseTime = GetTick() -- それ以外の状態は無関係なので時間だけ更新 end end end ------------------------------------------------------------------------- -- ラグチェック用関数 ------------------------------------------------------------------------- function CheckLag() ReturnFlag = true -- リターンフラグを立てる local actors = GetActors() for i,v in ipairs(actors) do if GetV(V_MOTION,v) ~= MOTION_STAND and GetV(V_MOTION,v) ~= MOTION_SIT and GetV(V_MOTION,v) ~= MOTION_CASTING then -- 立ちか詠唱以外のモーションが居たら LagCheckFlag = false ReturnFlag = false -- リターンフラグを戻す TraceAI("--- Lag out") return end end TraceAI(" !!! Lag ???") end ------------------------------------------------------ -- ステータスチェック ------------------------------------------------------ function StatusCheck() -- HP/SP等の取得 local owner = GetV(V_OWNER,MyID) local homHP = GetPerHP(MyID) local homSP = GetPerSP(MyID) local ownerHP = GetPerHP(owner) local ownerSP = GetPerSP(owner) -- ホムHPチェック if homHP < HomunculusSafetyHP then TriggerControl(TRIGGER_ON,MY_HP_UNDER_SAFETY) else TriggerControl(TRIGGER_OFF,MY_HP_UNDER_SAFETY) end -- ホムSPチェック if homSP < HomunculusSafetySP then TriggerControl(TRIGGER_ON,MY_SP_UNDER_SAFETY) else TriggerControl(TRIGGER_OFF,MY_SP_UNDER_SAFETY) end -- 主人HPチェック if ownerHP < OwnerSafetyHP then TriggerControl(TRIGGER_ON,OWNER_HP_UNDER_SAFETY) else TriggerControl(TRIGGER_OFF,OWNER_HP_UNDER_SAFETY) end -- 主人SPチェック if ownerSP < OwnerSafetySP then TriggerControl(TRIGGER_ON,OWNER_SP_UNDER_SAFETY) else TriggerControl(TRIGGER_OFF,OWNER_SP_UNDER_SAFETY) end end ------------------------------------------------------ -- HPの%チェック ------------------------------------------------------ function GetPerHP(id) local hp = GetV(V_HP,id) local mhp = GetV(V_MAXHP,id) return (hp/mhp)*100 end ------------------------------------------------------ -- SPの%チェック ------------------------------------------------------ function GetPerSP(id) local sp = GetV(V_SP,id) local msp = GetV(V_MAXSP,id) return (sp/msp)*100 end ------------------------------------------------------ -- 主人との距離チェック ------------------------------------------------------ function CheckDistance() local x,y = GetV(V_POSITION,MyID) local ox,oy = GetV(V_POSITION,GetV(V_OWNER,MyID)) if MathABS(x - ox) > 12 or MathABS(y - oy) > 12 then -- 距離が12より多くなったら if MathABS(x - ox) < 15 or MathABS(y - oy) < 15 then -- 14以内なら MyOwner = GetV(V_OWNER,MyID) -- 主人を取得しなおして ResetEnemy() -- リセットしつつ Move(MyID,ox,oy) -- 主人のセルへ戻る ReturnFlag = true -- リターンフラグ --TraceAI(" !! Long distance --------------- Back To Owner.") end elseif IsInSight(MyOwner) == 0 then MyOwner = GetV(V_OWNER,MyID) end end ------------------------------------------------------ -- 自分のタイプチェック ------------------------------------------------------ function CheckMyType(my,high) local ty = 0 if my == LIF or my == LIF_H or my == LIF2 or my == LIF_H2 then ty = 1 elseif my == AMISTR or my == AMISTR_H or my == AMISTR2 or my == AMISTR_H2 then ty = 2 elseif my == FILIR or my == FILIR_H or my == FILIR2 or my == FILIR_H2 then ty = 3 elseif my == VANILMIRTH or my == VANILMIRTH_H or my == VANILMIRTH2 or my == VANILMIRTH_H2 then ty = 4 end if high then -- 進化後の区別をする場合 if my == LIF_H or my == LIF_H2 or my == AMISTR_H or my == AMISTR_H2 or my == FILIR_H or my == FILIR_H2 or my == VANILMIRTH_H or my == VANILMIRTH_H2 then ty = ty + 8 end end return ty end ------------------------------------------------------ -- トーク出力関数 -- actor:種ID case:状況 value:値(内容に応じる) hold:表示時間(秒) トークメッセージは強制的にinter ------------------------------------------------------ function PrintTalkMSG(actor,case,value,hold) if PrintTalk then PrintMessage(200,actor,case,value,hold,1) end end ------------------------------------------------------ -- メッセージ出力関数 -- state:状態 actor:対象ID case:状況 value:値(内容に応じる) hold:表示時間(秒) inter:1で前回の表示時間をキャンセル(即表示) ------------------------------------------------------ function PrintMessage(state,actor,case,value,hold,inter) if PrintMsg and PrintableFlag then local pass = os.difftime(os.time(),PrintTimeOut) if inter == 1 then MsgHoldTime = -1 end if state == msgList[1] and actor==msgList[15] then return elseif pass > MsgHoldTime then -- 前回と違う状態になった&1秒以上経ったらメッセージ更新 msgList = {-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0} -- リストクリア msgList[1] = state if state == IDLE_ST then if ActiveFlag then msgList[2] = 1 end elseif state == CHASE_ST then msgList[2] = GetV(V_HOMUNTYPE,actor) elseif state == ATTACK_ST or state == SHOOTING_ST then local id = GetV(V_HOMUNTYPE,actor) if not Mob[id] then Mob[id] = {} end msgList[2] = id -- 敵の種ID if Mob[id][M_ACT] then msgList[3] = Mob[id][M_ACT] else msgList[3] = 3 end -- 対応 if Mob[id][M_SKILL] then if Mob[id][M_SKILL] == 6 or Mob[id][M_SKILL] == 12 then msgList[4] = A_SkillLevel+AdditionalLevel -- スキルレベル if msgList[4] > 5 then msgList[4] = 5 elseif msgList[4] < 0 then msgList[4] = 0 end else msgList[4] = Mob[id][M_SKILL] end end if Mob[id][M_CAST] then msgList[5] = Mob[id][M_CAST] end -- 詠唱妨害 if Mob[id][M_BOOST] then msgList[6] = Mob[id][M_BOOST] end -- 特殊強化 if Mob[id][M_RANK] then msgList[7] = Mob[id][M_RANK] end -- 優先度 if Mob[id][M_RECORD] then msgList[8] = Mob[id][M_RECORD] end -- 記録 msgList[9] = math.floor(AutoSkill+((GetV(V_SP,MyID)/GetV(V_MAXSP,MyID))^2*10)) -- ASP if msgList[9] > 100 then msgList[9] = 100 end msgList[15] = actor elseif state == EDIT_ST then msgList[2] = case -- 編集内容 msgList[3] = value -- 更新データ msgList[4] = actor -- 対象ID msgList[5] = GetV(V_HOMUNTYPE,actor) -- 対象の種ID msgList[6] = IsHomunculus(actor) -- ホムかどうか elseif state == TRIGGER_ST then msgList[2] = case -- 編集内容 msgList[3] = value -- 更新データ msgList[4] = actor -- 対象ID msgList[5] = GetV(V_HOMUNTYPE,actor) -- 対象の種ID msgList[6],msgList[7] = GetV(V_POSITION,actor) -- 対象の座標 msgList[8] = IsMonster(actor) -- 対象は攻撃可能か elseif state == MODE_ST then msgList[2] = case -- モード番号 msgList[3] = actor -- モードインデックス elseif state == 200 then -- トーク専用 msgList[2] = case -- メッセージ内容 msgList[3] = value -- メッセージ内容 end msgList[10] = MyType local filename = SaveDir.."Message.lua" local fp = io.open(filename,"w") if fp then fp:write(string.format("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", msgList[1], msgList[2], msgList[3], msgList[4], msgList[5], msgList[6], msgList[7], msgList[8], msgList[9], msgList[10], msgList[11], msgList[12], msgList[13], msgList[14], msgList[15])) PrintTimeOut = os.time() if hold then MsgHoldTime = hold else MsgHoldTime = 0 end fp:close() PrintableFlag = false -- 1サイクルで2回以上書き込めない end end end end ------------------------------------------------------ -- モードシフト関数 number に対応するタイプを読み込む ------------------------------------------------------ function ShiftMode(list) local length = TableSize(list) if ModeList[ModeIndex] == CheckMyType(MyType) then -- リストに自分のタイプと同じ番号が含まれていたら ModeIndex = ModeIndex +1 end if ModeIndex <= length then TraceAI(" Shift ! number :"..ModeList[ModeIndex]) ChangeMode(ModeList[ModeIndex]) ModeIndex = ModeIndex +1 else ModeIndex = 0 local om = CheckMyType(GetV(V_HOMUNTYPE,MyID)) TraceAI(" Original mode ! number :"..om) ChangeMode(om) ModeIndex = 1 end end ------------------------------------------------------ -- モードチェンジ関数 number に対応するタイプを読み込む ------------------------------------------------------ function ChangeMode(number) if number > 0 and number < 5 then if io.open(SaveDir.."Set.lua") then local m_filename = MobFilename -- Mobファイルはそのまま MyType = number -- 自分のタイプを偽装して Setting() MyType = GetV(V_HOMUNTYPE,MyID) -- 元に戻す MobFilename = m_filename -- 元に戻す PrintMessage(MODE_ST,ModeIndex,number,0,1,1) end elseif number == 0 then Initialize() end end -- Settingダミー関数 function Setting() end ------------------------------------------------------ -- 自律行動時のメインルーチン ------------------------------------------------------ function AImain(myid) MyID = myid -- リターンフラグのチェック ReturnFlag = false PrintableFlag = true if InitialFlag then Initialize() PrintMessage(-1,0,0,0,0,1) end if SleepFlag then if InitializedTime + SleepTime < GetTick() then -- 起動後1秒経ったら LoadRecord(MobFilename) -- 戦闘記録読み込み SleepFlag = false -- スリープ終わり end return -- 起動から1秒間は何もしない end AttackAccelerator(3) -- APSD補正 local msg = GetMsg (myid) -- コマンドを受けたときの処理 local rmsg = GetResMsg (myid) -- 予約コマンドを受けたときの処理 if (msg[1] ~= NONE_CMD) then -- 手動コマンドがあれば ProcessCommand(msg) -- -> 手動コマンド処理 elseif (rmsg[1] ~= NONE_CMD) then -- 予約コマンドがあれば ReservedCommand(rmsg) -- -> 予約コマンド処理 end local ox,oy = GetV(V_POSITION,MyOwner) SaveLastWalk (ox,oy) -- 主人の足跡監視 StatusCheck() -- ステータスチェック if WaitSPR then WaitUntilSPR() -- SPR待ち end if LagCheckFlag then -- ラグチェック CheckLag() else CheckStack() -- 移動詰まり解消 end MC_AlertCheck() -- MChアラート if MyState ~= IDLE_ST then -- 旋回動作用調整 RondEnd = os.time() if MyState ~= ALERT_ST and MyState ~= ESCAPE_ST and MyState ~= SHOOTING_ST and MyState ~= SEARCH_ST then PatrolVector = 0 end end CheckDistance() -- 主人との距離チェック if ReturnFlag then -- 状態処理前に一旦ループを終わらせたい場合 TraceAI(" ====------->>> exit") return end if AutoRecoverSkill and AutoSkillSPLimit < GetPerSP(MyID) then -- オートリカバーONかつSPが一定割合以上 if GetPerHP(GetV(V_OWNER,MyID)) < AutoRecoverHP then -- 主人かホムのHPが一定割合以下 local pass = os.difftime(os.time(),BufferTime) if RecoverDelay < GetTick() and AvoidDelay < pass then AutoSkill_HEAL() end else FirstAid = true end end -- 状態処理 if (MyState == ALERT_ST) then OnALERT_ST () end if (MyState == IDLE_ST) then OnIDLE_ST () end if (MyState == ESCAPE_ST) then OnESCAPE_ST () end if (MyState == CHASE_ST) then OnCHASE_ST () elseif (MyState == HOOK_ST) then OnHOOK_ST () end if (MyState == ATTACK_ST) then OnATTACK_ST () elseif (MyState == SHOOTING_ST) then OnSHOOTING_ST () end if (MyState == FOLLOW_ST) then OnFOLLOW_ST () elseif (MyState == FORWARD_ST) then OnFORWARD_ST () elseif (MyState == MOVE_CMD_ST) then OnMOVE_CMD_ST () elseif (MyState == FOLLOW_CMD_ST) then OnFOLLOW_CMD_ST () elseif (MyState == SEARCH_ST) then OnSEARCH_ST() end if (MyState == ATTACK_OBJECT_CMD_ST) then OnATTACK_OBJECT_CMD_ST () end AttackAccelerator(2) -- ASPD補正 if AutoExit and GetTick() - ExitCheckTime > 2000 then -- 寝落ちチェック ExitEvent() end end -- エラーが出たら空のAI関数に差し替える function try(func, ...) local ok, err = pcall(func, unpack(arg)) if(not ok) then ErrorOut("Error.txt", err) -- エラー内容をファイルへ PrintMessage(99,0,0,0,0,1) AI = function() end -- エラーが出たらAI()関数を空にする end end -- エラー内容をファイルに出力 function ErrorOut(str1 ,str2) local fp = io.open(string.format(SaveDir.."%s", str1), "a") if(fp == nil) then return end fp:write(string.format("%s : %s\n", os.date("%Y/%m/%d %H:%M:%S"), str2) ) fp:close() end -- メイン function AI(id) try(AImain, id) end