---------------こっこAI⊂(*'ω')⊃ Ver4.10 06/12/30--------------------------------------------- -- 機能詳細,設定マニュアルなどは以下のURLから確認してください. -- http://privatemoon.sakura.ne.jp/alc/cocco_manual.html ----------------------------Editing by 冠月ユウ----------------------------------------------------- ------------------------------------------------- -- 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 ---------------------------- -------------------------- -- 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のモーション -------------------------- -- Util.luaと同等の内容-------------------------------------------------------------------------------------------- -------------------------------------------- -- List utility -------------------------------------------- List = {} function List.new () return { first = 0, last = -1} end function List.pushleft (list, value) local first = list.first-1 list.first = first list[first] = value; end function List.pushright (list, value) local last = list.last + 1 list.last = last list[last] = value end function List.popleft (list) local first = list.first if first > list.last then return nil end local value = list[first] list[first] = nil -- to allow garbage collection list.first = first+1 return value end function List.popright (list) local last = list.last if list.first > last then return nil end local value = list[last] list[last] = nil list.last = last-1 return value end function List.clear (list) for i,v in ipairs(list) do list[i] = nil end --[[ if List.size(list) == 0 then return end local first = list.first local last = list.last for i=first, last do list[i] = nil end --]] list.first = 0 list.last = -1 end function List.size (list) local size = list.last - list.first + 1 return size end ---------------------------------------------------------------------------------------------- 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 IsInAttackSight (id1,id2) local x1,y1 = GetV (V_POSITION,id1) local x2,y2 = GetV (V_POSITION,id2) if (x1 == -1 or x2 == -1) then return false end local d = GetDistance (x1,y1,x2,y2) local a = 0 if (MySkill == 0) then a = GetV (V_ATTACKRANGE,id1) else a = GetV (V_SKILLATTACKRANGE,id1,MySkill) end if a >= d then return true; else return false; end end ------------------------------------------ -- グローバル変数(オリジナル) ------------------------------------------ MyState = IDLE_ST -- 最初の状態は休息 MyEnemy = 0 -- 敵 id MyDestX = 0 -- 目的地 x座標 MyDestY = 0 -- 目的地 y座標 MyPatrolX = 0 -- 偵察目的地 x座標 MyPatrolY = 0 -- 偵察目的地 y座標 ResCmdList = List.new() -- 予約コマンドリスト MyID = 0 -- ホムンクルス id MySkill = 0 -- ホムンクルスのスキル MySkillLevel = 0 -- ホムンクルスのスキルレベル ------------------------------------------ -- SelectActors用グローバル定数 --------------------------------------------------------------------------------------------------------- SELECT_TARGET = 1 SELECT_MY_TARGET = 2 SELECT_OWNER_TARGET = 3 SELECT_FREE_TARGET = 4 SELECT_MOTION = 5 SELECT_NOT_MOTION = 6 SELECT_ENEMYS = 7 SELECT_IN_RANGE = 8 SELECT_OUT_RANGE = 9 SELECT_PLAYERS = 10 -- 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 -- └スキルを使う(デフォルト設定) M_AS = 3 -- オートスキルの個別基本確率設定 M_CAST = 4 -- 詠唱妨害 CAST_OFF = 0 -- ├しない CAST_ON = 1 -- └する M_BOOST = 5 -- 特殊強化系スキルに関して(MCh,OvS) BOOST_OFF = 0 -- ├使わない(デフォルト設定) BOOST_ON = 1 -- └使う M_RANK = 6 -- 個別索敵優先度 M_RECORD = 7 -- 平均戦闘時間用 InitialFlag = true function Initialize() TraceAI("----- Initialize ----- ") ------------------------------*-*-*-*-*-*-*-*-*-------------------------------- -- グローバル変数(カスタム) -- 数値を変更することで設定を変えられるものにはコメント先頭に●が付いています -- それ以外の変数は基本的に変更しないでください! ------------------------------------------------------------------------------- MyOwner = GetV(V_OWNER,MyID) -- 主人のID取得 OwnerX = 0 OwnerY = 0 -- 主人の座標保持 MyType = GetV(V_HOMUNTYPE,MyID) -- ホムンクルスのタイプを記録 MyPosX = 0 MyPosY = 0 SaveDir = "./AI/" -- ●セーブファイルディレクトリ -- 旋回動作に関する変数 --------------------------------------------------------------------------------------------------------- RondoFlag = true -- ●旋回動作を行うか否か(trueかfalse で設定 PatrolRest = 90 -- ●ホムの残りHP・SPがこの%以下の場合は静止(回復のため) PatrolInterval = 15000 -- ●一回転してから次に回転するまでの間隔 RondoPattern = 1 -- ●旋回動作のパターン 1:その場で小旋回 2:主人の回りを小旋回 RondoRadius = 1 -- ●旋回動作の半径 TimeCounter = 1 -- 時間カウント用変数 1以外にすると旋回動作を行いません RondEnd = os.time() -- 旋回間隔の時間管理 MoveDelay = 0 -- くるくる速度のための設定(初期値0のままでok) MoveSpeed = 150 -- くるくる速度係数 Rdx , Rdy = 0,0 -- くるくる目標座標 PatrolVector = 0 -- 現在のくるくる方向(初期値0のままでok) NextInterval = 0 -- 回転間隔計測のための設定(初期値0のままでok) StartTime = 0 -- 開始時間格納用変数 VectorEnd = 0 -- 移動回数設定 NowRondo = false -- 回転動作中 -- 等速追従に関する変数 --------------------------------------------------------------------------------------------------------- DifferentialAccuracy = 140 -- ●移動パケットを送る間隔(短いほど高負荷 長いほど低負荷だが追従が遅れる ChaceDistance = 2 -- ●主人を追跡する時にとる距離(1か2で設定.それ以外では不具合が起きます FollowDistance = 3 -- ●指定セルより離れたら追従を開始する DifferentialCount = 0 -- 間隔カウント用変数 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で設定 BufferLevel2 = 1 -- ●オートオーバードスピードの使用レベル(1〜5で設定 RecoverLevel = 3 -- ●オートリカバーの使用レベル(1〜5で設定 AutoRecoverHP = 50 -- ●オートリカバーが発動するHPの割合(%) EmergencyAvoid = false -- ●移動時に自動で緊急回避を使用するか否か(trueかfalse で設定 EA_Level = 1 -- ●自動緊急回避の使用レベル(1〜5で設定 BloodLust = false -- ●自動強化スキルでアミストル種がブラッドラストを使うか否か(falseならディフェンスを使う EditNoSkill = true -- ●ムーンライト(カプリス)Lv1を手動で使うことで,対象をスキル自動非使用Mobに登録 MentalChangeAlert = 20 -- ●メンタルチェンジの終了時間が指定秒前になると旋回動作でアラートする(0ならアラートしない SBR = false -- ●オートスキルのムーンライトをS.B.R44に置き換える StopAS = true -- ●平均戦闘時間により戦闘の途中でASを止めるか否か ASCutTimeRate = 90 -- ●平均戦闘時間の指定%以上の交戦時間ではASしない AttackSkillDelay = 0 -- オートムーンライト用 SkillDelay = 2500 -- スキルディレイ管理 AS_SetFlag = true -- スキルセット用フラグ BufferDelay = 0 -- オートフリットムーブ用ディレイタイム BufferTime = 0 -- 自動強化スキル時間管理 BufferDelay2 = 0 -- オートオーバードスピード用ディレイタイム BufferTime2 = 0 -- 自動強化スキル時間管理 RecoverDelay = 0 -- オートリカバー用ディレイタイム AvoidDelay = 0 -- オート緊急回避用ディレイタイム A_Skill = 0 -- オートムーンライトを設定する変数 A_SkillLevel = 0 -- オートスキルレベル設定 MentalChange = false -- メンタルチェンジ中 SkillTimeOut = true -- ASの時間制限管理 UseSkillFlag = false -- オートスキル使用フラグ -- スキル使用に関する変数 --------------------------------------------------------------------------------------------------------- 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を指定 FirstAttackFlag = true -- 初撃管理フラグ(機能ON/OFFのフラグはこちらではないので注意) SkillMotionDelay = 0 -- スキルモーションディレイ -- AS確率操作に関する変数 --------------------------------------------------------------------------------------------------------- ASBorder_N = 3 -- ●敵の数が指定数以上のとき,AS確率上昇 ASP_Increase = 30 -- ●↑の時,上昇させる%値 ASL_Increase = 1 -- ●↑の時,上昇させるスキルLv ASBorder_T = 7000 -- ●敵の平均戦闘時間が指定ミリ秒以上のとき,AS確率上昇 ASP_Increase_T = 5 -- ●↑の時,上昇させる%値 ASL_Increase_T = 0 -- ●↑の時,上昇させるスキルLv ASP_Neutral = AutoSkill -- 確率初期値 EnemyNum = 1 -- 判定用定数 EnemyType = 2 -- 判定用変数 -- 退避行動に関する変数 --------------------------------------------------------------------------------------------------------- EscapeFlag = true -- ●逃避行動をするか否か HomunculusSafetyHP = 30 -- ●単位%:ホムのHPがこれ以下の場合に逃避行動 EscapePattern = 0 -- ●ホム危険時,敵から退避するパターン EscapeRange = 3 -- ●ホム危険時,主人の何マス後ろへ隠れるか EscapeDirection = 0 -- ●後ろへ隠れる設定の場合,後ろ以外の方向に変更 EmergencyBoost = false -- ●ホム危険時,退避ではなくブーストをかける ASP_Emergency = 80 -- ●ブースト時のAS確率上昇 ESCAPE_ST = 25 -- 逃避行動状態の状態変数 LastMoveTime = 0 -- 最後に移動した時間を記録 -- アクティブ状態に関する変数 --------------------------------------------------------------------------------------------------------- TargetDistance = 7 -- ●タゲに選択する敵のケミからの距離の制限 3〜14程度なら動くはず ActiveFlag = false -- ●アクティブデフォルト設定 falseがノンアクティブ trueがアクティブ ActiveRange = true -- ●索敵範囲を他人との距離に応じて変化させるか否か RangeDecreaseRate = TargetDistance*3 -- ●索敵範囲絞りの減少係数(値が大きい程減り幅が大きい.14以下にすると逆に増えるので注意 LimitRangeDist = 6 -- ●この距離以内に人が居る場合は,アクティブ索敵しない HalfActive = true -- ●任意のMobはアクティブに攻撃しにいく ActiveHP = 40 -- ●指定%以下のHPの時はノンアクティブ OthersRange = 4 -- ●ターゲットの指定セル以内に他プレイヤーが居る場合はターゲットリセット OnActive = false -- アクティブ索敵中フラグ -- 通常攻撃に関する変数 --------------------------------------------------------------------------------------------------------- FollowOwnerTargetting = false -- ●主人のタゲを最優先にするかどうか (する→true  しない→false OnBattleDistance = 7 -- ●戦闘中、この距離以上主人が離れると戦闘を中断して追従 OnBattleDistancePlus = 10 -- ●友達が居る場合、OnBattleDistanceを指定数のセルまで拡張する.拡張不要であれば上記と同値に. OBD_Neutral = OnBattleDistance -- 初期値を記憶 AttackDistance = 1 -- ●攻撃する時の敵との距離.(1か2以外では不具合の恐れ Aspd = 120 -- ●攻撃速度調整 AdjustASPD = false -- ●モーションを正しく表示できるようにASPD調整 MultiAttack = 2 -- ●1サイクルの攻撃回数(攻撃モーションキャンセル:値域1〜3,数字の数だけ攻撃コマンド LastAttack = 0 -- 前回攻撃した時の時間 OverlapFlag = 0 -- 戦闘中、敵と重なったときに移動するためのフラグ変数 AwayStepTime = 0 -- 座標重複を回避する時の時間的制御用変数 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 = 30 -- ●自動友達登録の懐きやすさ.20〜40前後がよいと思われる.低いとすぐ登録するが,誤登録の可能性も. Neighbor = 0 -- 友達の可能性がある人のID NeighborFlag = false -- 管理フラグ NeighborTime = 0 -- 時間管理 FriendList = LoadList("Friend") -- 友達リスト FriendIndex = TableSize(FriendList) -- 友達リストの番号 FriendsEnemy = 0 -- 友達のターゲット FriendExists = false -- 近くに友達が居るか SameTargetCount = 0 -- タゲ被り回数 HomFriendList = {} -- ホムンクルスの友達リスト(ホムIDはロードごとリセット -- 射撃状態に関する変数 --------------------------------------------------------------------------------------------------------- ShootingMode = false -- ●戦闘は常に射撃状態で行うか否か ShootLevel = 3 -- ●射撃状態時に使用するスキルのスキルLv PatrolPattern = 6 -- ●射撃状態時に接敵された場合,退避する旋回パターン(待機時旋回と同様) PatrolRange = 3 -- ●射撃状態時に接敵された場合,退避する距離(主人からの距離) SHOOTING_ST = 26 -- 状態変数 ShootDelay = 0 -- ディレイ管理用変数 -- 寝落ち防止機能に関する変数 ---------------------------------------------------------------------------- AutoExit = false -- ●寝落ち防止を利用するか否か ExitLimitTime = 1800 -- ●寝落ち防止を何秒で実行するか(デフォは1800秒=30分 ExitCheckTime = 0 -- 寝落ちチェック間隔管理 -- 共闘モード関する変数 ---------------------------------------------------------------------------- BonusMode = false -- ●共闘モードにするかどうか BonusTime = 500 -- ●指定ミリ秒間だけ攻撃する -- 詠唱妨害関する変数 ---------------------------------------------------------------------------- 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 -- 沈黙状態自動チェックに関する変数 ---------------------------------------------------------------------------- AutoCheck_Silence = false -- ●沈黙状態自動判定をするか否か SkillFailedCount = 0 -- スキル失敗カウント Silencer = false -- 沈黙スイッチ SilenceTime = 0 -- 沈黙時間管理 ST_BaseTime = 30000 -- ★沈黙の基本持続時間(ms)(60秒?) HomVIT = 20 -- ★ホムンクルスのVIT -- 先行移動に関する変数 ---------------------------------------------------------------------------- OnForward = false -- ●移動時にホムンクルスが先導するか否か ForwardDistance = 3 -- ●先行する距離 PrevPos1x, PrevPos1y = GetV(V_POSITION,MyOwner) -- 前回居た位置 PrevPos2x, PrevPos2y = GetV(V_POSITION,MyOwner) -- 前々回居た位置 PrevPos3x, PrevPos3y = GetV(V_POSITION,MyOwner) -- 前々々回居た位置 FORWARD_ST = 28 -- 先導状態定数 -- スタックチェックに関する変数 ---------------------------------------------------------------------------- AttackFalseCheck = true -- ●攻撃スタックをチェックする StandTime = 0 -- 停止時間 AttackFalseCount = 0 -- 攻撃ミス回数 AttackFalseTime = GetTick() -- 攻撃ミス時間 SkipStack = false -- 索敵優先度に関する変数 --------------------------------------------------------------------------------------------------- -- 詳細はマニュアルを参照のこと EnemyPriority = {3, 2, 1, 5, 4} -- ●敵選択優先度の設定.配列内の数字の順位で設定.条件を4つ以下に設定しても有効 RankedEnemySelect = 1 -- ●フリーの敵選択時,優先度設定(0:一番近い敵,1:一番弱い敵,2:一番強い敵 -- その他の変数 --------------------------------------------------------------------------------------------------------- MoveToStay = false -- ●移動命令後,休息状態にする SleepFlag = true -- ●スリープフラグ(falseにするとスリープしない SleepTime = 1000 -- ●スリープ時間(ミリ秒) QuickTrigger = false -- ●Alt+T連打によるスイッチを使うか否か StayFlag = false -- 移動命令時の留まりを管理 AllEnemys = {} -- 敵リスト LostEnemy = 0 -- さっき倒した敵 StepCommand = {0,0,0} -- ステップコマンド格納 SCIndex = 0 -- ステップコマンド用インデックス ALERT_ST = 27 -- アラートパフォーマンス用 T_TriggerTime = 0 -- Alt+Tタイミング管理 ReturnFlag = false -- 移動補正フラグ ManualAttack = false -- 手動攻撃フラグ -- 平均戦闘時間に関する変数 ---------------------------------------------------------------------------- MobFilename = "Mob" RecordBattleTime = true -- ●敵ごとに平均戦闘時間を記録する RecCount = 10 -- ●戦闘時間の平均を取るサンプル数 KillCount = {} DistTime = {} OnBattleTime = 0 -- 念属性チェック用 ---------------------------------------------------------------------------- -- オートSBRを使用するとき,以下のMobに対してはムーンライトに切り替える -- ナイトメア,エギラ,ゴーストリング,マリオネット,ウィスパー,巨大ウィスパー,コウ,黒蛇王,クラベン, -- ノクシャス,カトリーヌ=ケイロン,ラウレル=ヴィンダー,プラズマ(念),タナトスシリーズ,魔剣士タナトス GhostMob = {1061,1116,1120,1143,1179,1185,1186,1415,1418,1427,1459,1524,1529,1554,1576,1587,1599,1620,1639,1645,1651,1657,1663,1693,1704,1705,1706,1707,1708,1709,1710,1711,1712} ASP_Decrease_G = 100 -- ●対念MobでSBR→MLに切り替わった時に,AS確率を指定値だけ減少させる ChangeGhost = false -- 念属性変化対応用フラグ -- 外部設定ファイル読み込み --------------------------------------------------------------------------------------------------------- if (io.open(SaveDir.."Set.lua") ~= nil) then local setFile = SaveDir.."Set.lua" require (setFile) TraceAI(" ! ReadFile : "..SaveDir.."Set.lua") -- 設定ファイル読み込み後の再定義 ---------------------------------------------------------------------------------------------------- ASP_Neutral = AutoSkill -- 確率初期値 OBD_Neutral = OnBattleDistance -- 初期値を記憶 RangeDecreaseRate = TargetDistance*3 end if (io.open(SaveDir.."CustomCommand.lua") ~= nil) then local cmdFile = SaveDir.."CustomCommand.lua" require (cmdFile) TraceAI(" ! ReadFile : ./AI/CustomCommand.lua") CustomCommand = true end Mob = {} -- 配列設定 local m_index = 1000 while m_index < 3000 do m_index = m_index + 1 Mob[m_index] = {} end LoadRecord(MobFilename) -- 戦闘記録読み込み InitializedTime = GetTick() InitialFlag = false TraceAI("----- Initialized ----- ") end -- 設定変数ここまで ----------------------------------------------------------------------------------------------- -- ------------------------------------------------------ -- -- リストをロードする -- ------------------------------------------------------ -- function LoadList(file) local filename = SaveDir..file..".ini" local list = {} fp = io.open(filename,"r") -- ファイルを開く if (fp ~= nil) then while true do local line = fp:read() -- 一行ずつ読み込み if (line == nil) then break -- 空行があったら抜ける end local num = {} local p = 0 for n in string.gfind(line, "%d+") do p = p + 1 num[p] = tonumber(n) end if not num[2] then num[2] = 0 end list[num[1]] = num[2] -- 格納 end fp:close() TraceAI(" ! ReadFile : "..filename) else io.output(filename) -- 無ければ作る TraceAI(" ! MakeFile : "..filename) end return list end ---------------------------------------------------------- -- リストをセーブする ---------------------------------------------------------- function SaveList(file,list) local filename = SaveDir..file..".ini" fp = io.open(filename,"w") if (fp ~= nil) then -- ファイルが既にあれば for i,v in pairs (list) do fp:write(string.format("%d %d\n",i, v)) -- 一行ずつ書き出し end end fp:close() 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 -----------Origenal Function--------------- ------------------------------------------------------- -- 旋回ダンスに関する定義 ------------------------------------------------------- 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) -- 座標取得 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 -- アラート用 rondDist = {{x=OwnerX-1,y=OwnerY},{x=OwnerX,y=OwnerY+1},{x=OwnerX+1,y=OwnerY},{x=OwnerX,y=OwnerY-1}} VectorEnd = 4 end end ------------------------------------------------------- -- MoveToNearについて定義 -- IDからDist離れた位置へ移動.Distは1か2推奨 ------------------------------------------------------- function MoveToNear (id,dist) local x,y = GetV(V_POSITION,id) if not SPR_Flag or MyState == CHASE_ST then if (GetDistance2(MyID,id) > 5 and id == MyOwner) then Move (MyID,x,y) else local mx,my = GetV(V_POSITION,MyID) if (math.abs(x - mx) > dist) then if (x < mx) then x = x + dist else x = x - dist end else x = mx end if (math.abs(y - my) > dist) then if (y < my) then y = y + dist else y = y - dist end else y = my end Move (MyID,x,y) end end end ------------------------------------------------------- -- 移動先の位置をテンキーの数値で表現し,返す関数 ------------------------------------------------------- function InputCommand(x,y) OwnerX, OwnerY = GetV(V_POSITION,GetV(V_OWNER,MyID)) if x == OwnerX and y == OwnerY then -- 中央:主人の座標(5) return 5 elseif 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 -- →↓→でシューティングスター SkillObject (MyID,5,8011,MyID) -- オーバードスピードLv1 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") if MyOwner == GetV(V_OWNER,MyID) then if FriendIndex > 0 then local actors = GetActors() actors = GetFriends(actors) local owner = GetNearActorID(MyID, actors) if owner then MyOwner = owner -- 友達が居る時は、一番近い友達に委譲 end else MyOwner = GetPlayersNearID(4) -- 居ないときは4セル以内の一番近い人に委譲 end else MyOwner = GetV(V_OWNER,MyID) -- 2回目で元の主人に戻る end TraceAI ("!! NewMaster : "..MyOwner) MyState = ALERT_ST -- 偽主人の周りを旋回 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 -- 主人の周りを旋回して合図 end if StepCommand[1] == 5 and StepCommand[2] == 5 and StepCommand[3] == 5 then -- Gvモード専用コマンド if GvMode then TraceAI ("--> Command Succses!! Add All GuildMember") local actors = GetActors() AddAllGuildMembers(actors) end 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 if ( x == MyDestX and y == MyDestY and MOTION_MOVE == GetV(V_MOTION,MyID)) then return -- 目的地と現在地が同一の場合は、処理しない end local curX, curY = GetV (V_POSITION,MyID) if (math.abs(x-curX)+math.abs(y-curY) > 15) then -- 目的地が一定距離以上なら (サーバーで遠距離は処理しないため) List.pushleft (ResCmdList,{MOVE_CMD,x,y}) -- 元の目的地への移動を予約する x = math.floor((x+curX)/2) -- 中間地点へ移動する y = math.floor((y+curY)/2) -- end Move (MyID,x,y) if MyState ~= MOVE_CMD_ST then LastState = MyState end MyState = MOVE_CMD_ST MyDestX = x MyDestY = y MyEnemy = 0 MySkill = 0 end ------------------------------------------------------- -- 停止コマンドを受けた時に関する定義 ------------------------------------------------------- function OnSTOP_CMD () TraceAI ("OnSTOP_CMD") if (GetV(V_MOTION,MyID) ~= MOTION_STAND) then Move (MyID,GetV(V_POSITION,MyID)) end MyState = IDLE_ST MyDestX = 0 MyDestY = 0 MyEnemy = 0 MySkill = 0 end ------------------------------------------------------- -- 通常攻撃コマンドを受けた時に関する定義 ------------------------------------------------------- function OnATTACK_OBJECT_CMD (id) TraceAI ("OnATTACK_OBJECT_CMD") MySkill = 0 MyEnemy = id MyState = CHASE_ST ManualAttack = true -- Mob優先度編集 if IsMonster(id) == 1 and IsPlayer(id) == 0 and IsHomunculus(id) == 0 then -- 対象はMobである local rank = Mob[GetV(V_HOMUNTYPE,id)][M_RANK] if rank < 20 then -- 優先度が最大未満なら Mob[GetV(V_HOMUNTYPE,id)][M_RANK] = rank + 1 -- 優先度+1 SaveRecord(MobFilename) -- 保存 end end end ------------------------------------------------------- -- 地面指定攻撃コマンドを受けた時に関する定義 ------------------------------------------------------- function OnATTACK_AREA_CMD (x,y) TraceAI ("OnATTACK_AREA_CMD") if (x ~= MyDestX or y ~= MyDestY or MOTION_MOVE ~= GetV(V_MOTION,MyID)) then Move (MyID,x,y) end MyDestX = x MyDestY = y MyEnemy = 0 MyState = ATTACK_AREA_CMD_ST end ------------------------------------------------------- -- 対象指定スキルコマンドを受けた時に関する定義 ------------------------------------------------------- function OnSKILL_OBJECT_CMD (level,skill,id) if skill == 8012 and LimitSBR then -- スキルがS.B.Rだったら local num = TableSize(SBR_Target) for i,target in ipairs(SBR_Target)do if GetV(V_HOMUNTYPE,id) == target then -- リストにIDがあったら break -- ループ抜ける elseif i == num then -- 見つからずに最後まで来ちゃったら return -- スキルを使わない end end elseif EditNoSkill and IsMonster(id) == 1 then -- スキルLv1なら if level == 1 then AddNoSkill(id) elseif level == 2 then AddCast(id) end end TraceAI ("OnSKILL_OBJECT_CMD") Silencer = false -- サイレンサーOFF 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 OnSKILL_AREA_CMD (level,skill,x,y) TraceAI ("OnSKILL_AREA_CMD") Move (MyID,x,y) MyDestX = x MyDestY = y MySkillLevel = level MySkill = skill MyState = SKILL_AREA_CMD_ST end ------------------------------------------------------- -- 追従コマンドを受けた時に関する定義 ------------------------------------------------------- function OnFOLLOW_CMD () -- 待機命令は、待機状態と休息状態を互いに転換させる if (MyState ~= FOLLOW_CMD_ST) then ResetEnemy() MoveToNear (MyOwner,ChaceDistance) MyState = FOLLOW_CMD_ST --MyDestX, MyDestY = GetV (V_POSITION,MyOwner) MySkill = 0 if QuickTrigger then T_TriggerTime = GetTick() end else 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が押された場合 if OnForward then OnForward = false Response(0) else OnForward = true Response(1) end end end MyState = IDLE_ST MyEnemy , MyDestX , MyDestY = 0 , 0 , 0 -- 敵情報をリセット MySkill = 0 StayFlag = false end end ------------------------------------------------------- -- コマンドを受け付けたときの処理 ------------------------------------------------------- function ProcessCommand (msg) if (msg[1] == MOVE_CMD) then OnMOVE_CMD (msg[2],msg[3]) TraceAI ("MOVE_CMD") elseif (msg[1] == STOP_CMD) then OnSTOP_CMD () TraceAI ("STOP_CMD") elseif (msg[1] == ATTACK_OBJECT_CMD) then OnATTACK_OBJECT_CMD (msg[2]) TraceAI ("ATTACK_OBJECT_CMD") elseif (msg[1] == ATTACK_AREA_CMD) then OnATTACK_AREA_CMD (msg[2],msg[3]) TraceAI ("ATTACK_AREA_CMD") elseif (msg[1] == PATROL_CMD) then OnPATROL_CMD (msg[2],msg[3]) TraceAI ("PATROL_CMD") elseif (msg[1] == HOLD_CMD) then OnHOLD_CMD () TraceAI ("HOLD_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] == SKILL_AREA_CMD) then OnSKILL_AREA_CMD (msg[2],msg[3],msg[4],msg[5]) TraceAI ("SKILL_AREA_CMD") elseif (msg[1] == FOLLOW_CMD) then OnFOLLOW_CMD () TraceAI ("FOLLOW_CMD") end end ---------------------------------------------------------------------------------------------------------------------------------- -- アイドル中に関する定義 ---------------------------------------------------------------------------------------------------------------------------------- function OnIDLE_ST () TraceAI ("OnIDLE_ST") local cmd = List.popleft(ResCmdList) if (cmd ~= nil) then ProcessCommand (cmd) -- 予約コマンド処理 return end 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 <= (HomunculusSP / HomunculusMaxSP)*100 and PatrolRest <= (HomunculusHP / HomunculusMaxHP)*100 then if pass >= PatrolInterval then Rondo(RondoPattern,RondoRadius) end end end end -------------------------------------------------------------------------------------------------------------- -- 追従移動中に関する定義 -------------------------------------------------------------------------------------------------------------- function OnFOLLOW_ST () --TraceAI ("OnFOLLOW_ST") if (GetDistance2(MyID,MyOwner) <= FollowDistance) 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 MyType == LIF or MyType == LIF_H or MyType == LIF2 or MyType == LIF_H2 then -- リーフなら if AutoSkillSPLimit < (HomunculusSP / HomunculusMaxSP)*100 and HomunculusSP ~= 0 then AutoSkill_SELF(2) end end end if DifferentialCount < GetTick() then DifferentialCount = GetTick() + DifferentialAccuracy MoveToNear (MyOwner, ChaceDistance) -- 主人に追従する --TraceAI ("FOLLOW_ST -> FOLLOW_ST") return end end end -------------------------------------------------------------------------------------------------------------- -- 敵ターゲットをリセットする関数 -------------------------------------------------------------------------------------------------------------- function ResetEnemy() MyState = IDLE_ST -- 状態を待機へ MyEnemy = 0 -- ターゲットを0へ MyDestX, MyDestY = 0,0 -- 移動目標を0へ ChaceTime = 0 -- 追跡時間をリセット OnBattleTime = 0 -- 戦闘時間リセット OverlapFlag = 0 -- セル重なり回避フラグリセット AttackStCycle = 0 -- ATTACK_ST回数リセット FirstAttackFlag = true -- 初撃フラグリセット AutoSkill = ASP_Neutral -- AS確率リセット AS_SetFlag = true -- ASスキルセットフラグリセット SkillTimeOut = true -- ASタイムアウトリセット OnActive = false -- アクティブ索敵フラグリセット NowAwaying = false -- 退避中フラグリセット ManualAttack = false -- 手動攻撃フラグリセット if OnForward then ResetWalk() -- 足跡リセット end 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() MoveToOwner(MyID) MyState = FOLLOW_ST return end if not ManualAttack then -- 手動攻撃命令時ではないとき if not IsNotNoManner(MyID, MyEnemy) then -- 接近中に誰かターゲットを奪われたとき ResetEnemy() return end end -- Chaseの最中にスキルを使用するケース motion = GetV( V_MOTION, MyOwner ) if FA_OnChase and FirstAttackFlag and GetDistance2(MyID, MyEnemy) > AttackDistance then if motion ~= MOTION_SIT then UseFirstSkill() 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 !! ") end return end if GetDistance2(MyID, MyEnemy) <= AttackDistance or IsInAttackSight(MyID,MyEnemy) then -- 敵が攻撃射程範囲内に来たとき MyState = ATTACK_ST -- 攻撃に移る ChaceTime = 0 -- 追跡時間はリセット AttackFalseTime = GetTick() --TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN") return else MoveToNear(MyEnemy,AttackDistance) -- 敵の位置よりAttackDistanceだけ離れた位置へ --TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN") return end end ----------------------------------------------------------------------------------------------- -- アクティブスキルをセットする関数 -- スキルレベルとスキルを返す const : 固定スキルレベル ----------------------------------------------------------------------------------------------- function SetActiveSkill(const) local level = const local skill = 0 local enemy = GetV(V_HOMUNTYPE,MyEnemy) -- ホムンクルスの種族ごとにスキル設定 if (MyType == FILIR or MyType == FILIR_H or MyType == FILIR2 or MyType == FILIR_H2) then if SBR then -- ムーンライトの代わりにS.B.R44を使うとき if IsGhost(MyEnemy) == 0 and ChangeGhost==false then -- 念Mobでなければ skill = 8012 -- S.B.R44スキル(8012)は level = 3 -- レベルは最大 SkillDelay = 1 -- ディレイは0秒(仮) else skill = 8009 -- ムーンライトスキル(8009)は SkillDelay = 2500 -- ディレイは2.5秒 AutoSkill = AutoSkill - ASP_Decrease_G ChangeGhost = false -- 念チェンジ疑惑解消 end else skill = 8009 -- ムーンライトスキル(8009)は SkillDelay = 2500 -- ディレイは2.5秒 end elseif(MyType == VANILMIRTH or MyType == VANILMIRTH_H or MyType == VANILMIRTH2 or MyType == VANILMIRTH_H2) then skill = 8013 -- カプリススキル(8013)は SkillDelay = 1000+200*level -- ディレイは1000+200*Lv秒 else level = 0 skill = 0 end if skill ~= 0 and MyEnemy < 100000 then -- スキルが決まったらレベル決定 if Mob[enemy][M_SKILL] == nil or Mob[enemy][M_SKILL] == SKILL_AUTO then -- 個別設定が無い場合 if AutoAdjust then -- 自動調節するなら level = AdjustSkillLevel(enemy,const) -- 敵のタイプでスキルLvを調節 else -- 調節しない場合は level = const -- 設定したLvで発動 end elseif Mob[enemy][M_SKILL] >= 1 and Mob[enemy][M_SKILL] <= 5 then -- 個別設定がある場合 level = Mob[enemy][M_SKILL] -- 設定レベルに 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() if (A_Skill ~= 0 and A_SkillLevel ~= 0) then -- スキルがセットされていないと発動しない -- フィーリルとバニルミルト if (A_Skill == 8009 or A_Skill == 8012 or A_Skill == 8013) then -- 残りSPによって発動確率を調整 local AutoSkillProbability = AutoSkill + ((HomunculusSP / HomunculusMaxSP)^2 * 10) -- 賽を投げる local Dice = math.floor(math.random(100)) if (AutoSkillProbability > Dice) then SkillObject (MyID,A_SkillLevel,A_Skill,MyEnemy) -- スキル攻撃 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定 UseSkillFlag = true TraceAI (" !! Auto Attack Skill ") end end end end ----------------------------------------------------------------------------------------------- -- 自動スキル使用(アクティブ)の確率を操作する関数 -- case:状況(数による変化か,種類による変化か) pattern:判定(敵の数,種類) ----------------------------------------------------------------------------------------------- function ASP_Change(case,pattern) if pattern~= nil then if case == EnemyNum then -- こちらを攻撃する敵の数によって変化させる場合 if pattern >= ASBorder_N then -- 敵の数がボーダーより多ければ AutoSkill = AutoSkill + ASP_Increase -- 確率を増加 A_SkillLevel = A_SkillLevel + ASL_Increase -- Lv増加 end elseif case == EnemyType then -- 敵の種類によって変化させる場合(未実装 if pattern > ASBorder_T then AutoSkill = AutoSkill + ASP_Increase_T -- 確率を増加 A_SkillLevel = A_SkillLevel + ASL_Increase_T -- Lv増加 end end if A_SkillLevel > 5 then -- スキルLvの値が異常な場合は調整 A_SkillLevel = 5 elseif A_SkillLevel < 0 then A_SkillLevel = 0 end TraceAI(" ++ AutoSkillProbability : "..AutoSkill.." AS Level! : "..A_SkillLevel) end end ----------------------------------------------------------------------------------------------- -- 自動スキル使用(ブースト)の関数 -- case:1 フリットムーブ,ブラッドラスト,メンタルチェンジ,ディフェンス -- case:2 緊急回避 -- case:3 オーバードスピード ----------------------------------------------------------------------------------------------- function AutoSkill_SELF(case) if (MyType == FILIR or MyType == FILIR_H or MyType == FILIR2 or MyType == FILIR_H2) then -- フィーリル if case == 1 then MySkill = 8010 -- フリットムーブ(8010)は MySkillLevel = BufferLevel -- 指定Lvで常時発動 BufferDelay = 65 - BufferLevel*5 -- 持続時間計算 elseif case == 3 then MySkill = 8011 -- フリットムーブ(8010)は MySkillLevel = BufferLevel2 -- 指定Lvで常時発動 BufferDelay2 = 65 - BufferLevel*5 -- 持続時間計算 BufferTime2 = os.time() end elseif (MyType == AMISTR or MyType == AMISTR_H or MyType == AMISTR2 or MyType == AMISTR_H2) then -- アミストル if BloodLust then -- ブラッドラストの方を使う場合 MySkill = 8008 -- ブラッドラスト(8008)は MySkillLevel = BufferLevel -- 指定Lvで常時発動 BufferDelay = BufferLevel*300 -- 持続時間計算(Lv*5分) else MySkill = 8006 -- ディフェンス(8006)は MySkillLevel = BufferLevel -- 指定Lvで常時発動 BufferDelay = 45 - BufferLevel*5 -- 持続時間計算 end elseif MyType == LIF or MyType == LIF_H or MyType == LIF2 or MyType == LIF_H2 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 MyType == LIF_H or MyType == LIF_H2 then -- 進化リーフ MySkill = 8004 -- メンタルチェンジ(8004)は MySkillLevel = BufferLevel -- 指定Lvで常時発動 BufferDelay = BufferLevel*120 - 60 -- 持続時間計算 MentalChange = true end end 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) BufferTime = os.time() SkillMotionDelay = 300 + GetTick() MySkill = 0 MySkillLevel = 0 end end ----------------------------------------------------------------------------------------------- -- 自動スキル使用(回復)の関数 -- カオティックベネディクション,治癒の手 ----------------------------------------------------------------------------------------------- function AutoSkill_HEAL () local object = 0 if (MyType == VANILMIRTH or MyType == VANILMIRTH_H or MyType == VANILMIRTH2 or MyType == VANILMIRTH_H2) then -- バニル MySkill = 8014 -- カオティックベネディクション(8014)は MySkillLevel = RecoverLevel -- 指定Lvで発動 RecoverDelay = GetTick() + 1000 -- ディレイ設定 object = MyID -- 対象指定 elseif (MyType == LIF or MyType == LIF_H or MyType == LIF2 or MyType == LIF_H2) 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() actors = GetEnemys(actors) -- 周囲に敵が居るかチェック if actors[1] then -- 居たらスキルを使わないで抜ける MySkill = 0 MySkillLevel = 0 return end end if FirstAid then -- 初めてこの関数が回ってきたときの BeforeHP = GetV(V_HP,MyOwner) -- 主人のHP取得 TraceAI (" ---- OwnerHP : "..BeforeHP) end SkillObject (MyID,MySkillLevel,MySkill,object) -- スキル使用 TraceAI (" ** AutoRecovery!!") if MySkill == 8001 and FirstAid == false then -- スキルが治癒の手なら(2回目以降 local afterHP = GetV(V_HP,MyOwner) -- 主人の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 < (HomunculusSP / HomunculusMaxSP)*100 and HomunculusSP ~= 0 and IsMob(M_SKILL,MyEnemy) == 0) then local motion = GetV(V_MOTION,MyID) -- モーション取得(被ダメモーション以外ならスキル発動) if AutoBufferSkill and SkillTimeOut and motion ~= MOTION_DAMAGE then -- オート強化スキル local pass = os.difftime(os.time(),BufferTime) if AttackSkillDelay < GetTick() and BufferDelay <= pass and AvoidDelay < pass then -- スキルディレイ中でなければ AutoSkill_SELF(1) end end -- * アクティブスキル関連 if AS_SetFlag and AutoAttackSkill then -- スキルセットは1回だけでいい A_SkillLevel,A_Skill = SetActiveSkill(SetSkillLevel) -- スキルをセット if MyEnemy < 100000 then -- SBR以外の時 local enemy = GetV(V_HOMUNTYPE,MyEnemy) AutoSkill = AutoSkill + Mob[enemy][M_AS] -- 基本確率設定 local enemyCount = TableSize(AllEnemys) -- 敵の数検索 if enemyCount >= ASBorder_N then ASP_Change(EnemyNum,enemyCount) -- 敵の数でASの確率操作 end ASP_Change(EnemyType,Mob[enemy][M_RECORD]) -- 戦闘記録でAS確率操作 end AS_SetFlag = false -- スキル調整終了 end if SkillMotionDelay < GetTick() and motion ~= MOTION_DAMAGE then if AutoAttackSkill and AttackSkillDelay < GetTick() and SkillTimeOut then -- 使用フラグとディレイのチェック AutoSkill_OBJECT () -- オート攻撃スキル end -- +++ 初撃スキル +++ -- UseFirstSkill() end end end ----------------------------------------------------------------------------------------------- -- 初撃スキル用関数 ----------------------------------------------------------------------------------------------- function UseFirstSkill() if FirstAttack and FirstAttackFlag then -- 初撃スキル設定 if AttackSkillDelay < GetTick() then if (AutoSkillSPLimit < (HomunculusSP / HomunculusMaxSP)*100 and HomunculusSP ~= 0 and IsMob(M_SKILL,MyEnemy) == 0) then FA_SkillLevel,A_Skill = SetActiveSkill(FA_SkillLevel) -- スキルをセット if FA_SkillLevel ~= 0 then -- レベルが0を返されてなければ SkillObject (MyID,FA_SkillLevel,A_Skill,MyEnemy) -- スキル攻撃 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ --TraceAI (" !! First Attack !! ") end end FirstAttackFlag = false end end end ----------------------------------------------------------------------------------------------- -- 敵と座標が重なってる時に移動させる関数 ----------------------------------------------------------------------------------------------- function AvoidOverlap() local x,y = GetV(V_POSITION,MyID) if OverlapFlag < AwayCount and AwayStepTime < 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) -- 目標点へ移動 AwayStepTime = 140*AwayStep + GetTick() -- 移動ディレイ計算 TraceAI ("Overlapped!! count:"..OverlapFlag) return end end ----------------------------------------------------------------------------------------------- -- 指定の敵に対し詠唱妨害する関数 ----------------------------------------------------------------------------------------------- function PreventCasting(enemys) local preventTarget = 0 -- リストがあれば castingList = SelectActors(SELECT_MOTION,enemys,MOTION_CASTING) -- 詠唱中のMob castingList = GetEnemys(enemys) preventTarget = GetNearActorID(MyID, enemys) if preventTarget ~= 0 and preventTarget ~= MyEnemy then local skill = 0 local level = 0 skill,level = SetPreventSkill() if skill ~= 0 and AttackSkillDelay < GetTick() then -- スキルがセットされていて,ディレイがなければ SkillObject (MyID,level,skill,preventTarget) -- スキル攻撃で妨害 AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定(2.5秒くらい TraceAI(" /// Prevent Casting !! ") end end end function SetPreventSkill() local skill = 0 local level = 0 -- 詠唱妨害するためのスキルをセット if (MyType == FILIR or MyType == FILIR_H or MyType == FILIR2 or MyType == FILIR_H2) then if SBR then skill = 8012 -- S.B.R44スキル(8012)は SkillDelay = 1 -- ディレイは0秒 level = 3 -- Lv3で発動 else skill = 8009 -- ムーンライトスキル(8009)は SkillDelay = 2500 -- ディレイは2.5秒 level = PreventSkillLevel -- Lv1で発動 end elseif(MyType == VANILMIRTH or MyType == VANILMIRTH_H or MyType == VANILMIRTH2 or MyType == VANILMIRTH_H2) then skill = 8013 -- カプリススキル(8013)は SkillDelay = 1200 -- ディレイは1.2秒 level = PreventSkillLevel -- Lv1で発動 else level = 0 skill = 0 end return skill,level end ----------------------------------------------------------------------------------------------- -- 敵のチェックをする関数 ----------------------------------------------------------------------------------------------- function CheckEnemy(list) local enemys = list local num = TableSize(enemys) local actEscape = {} local escIndex = 0 local castOn = {} local castIndex = 0 local boostOn = {} local boostIndex = 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) -- 敵の種別ID if id ~= 0 and id ~= -1 then if Mob[id][M_ACT] == ACT_ESCAPE then -- 退避設定なら escIndex = escIndex + 1 actEscape[escIndex] = v end if Mob[id][M_CAST] == CAST_ON then -- 詠唱妨害ONなら castIndex = castIndex + 1 castOn[castIndex] = v end if Mob[id][M_BOOST] == BOOST_ON then -- 特殊強化ONなら boostIndex = boostIndex + 1 boostOn[boostIndex] = v end end end if actEscape[1] then -- 退避Mobが居たら EscapeFromDanger(actEscape) -- 特定退避行動 return end if PreventEnemyCasting and castOn[1] then -- 詠唱妨害ONなら if HomunculusSP > 30 then PreventCasting(castOn) return end end if boostOn[1] then local motion = GetV(V_MOTION,MyID) -- モーション取得(被ダメモーション以外ならスキル発動) if motion ~= MOTION_DAMAGE then local pass = os.difftime(os.time(),BufferTime2) if AttackSkillDelay < GetTick() and BufferDelay2 <= pass and AvoidDelay < pass then -- スキルディレイ中でなければ AutoSkill_SELF(3) -- オート特殊強化スキル return 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 -- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- -- 攻撃中に関する定義 -- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- function OnATTACK_ST () TraceAI ("OnATTACK_ST") AttackStCycle = AttackStCycle +1 if (AttackStCycle - math.floor(AttackStCycle/2)*2) == 1 then -- サイクルの2回に1回で AllEnemys = GetActors() -- 自分たちの敵を常に監視 AllEnemys = GetOurEnemys(AllEnemys) end CheckEnemy(AllEnemys) -- 敵への行動チェック if (GetDistance2(MyID,MyOwner) > OnBattleDistance) then -- 戦闘中、指定距離以上ケミが離れた場合、それに追従 MoveToNear(MyOwner,ChaceDistance) ResetEnemy() MyState = FOLLOW_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 if FollowOwnerTargetting then local target = GetV(V_TARGET,GetV(V_OWNER,MyID))-- ケミのタゲが変わった場合、それに追従する (すでに消えたIDはIsMonster==0) if target > 0 and IsMonster(target) == 1 and GetDistance2(MyOwner,target) < 3 and MyEnemy ~= target and IsMob(M_ACT,target) >= 0 then MyEnemy = target target = 0 OnBattleTime = 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 -- 攻撃対象が死んだとき LostEnemy = MyEnemy TraceAI (" ! LostEnemy : "..LostEnemy) if RecordBattleTime and not NowAwaying then RecordDist(100000 - MyEnemy) -- 戦闘記録( IDは死亡時点で10万ルールが適用されてる end ResetEnemy() -- 敵情報をリセット --TraceAI ("ATTACK_ST -> IDLE_ST") return end if IsOutOfSight(MyID,MyEnemy) then -- 敵が索敵範囲外に出たとき ResetEnemy() -- 敵情報をリセット return end if NowAwaying then -- 退避対象敵が居る場合 return end if GetDistance2(MyID, MyEnemy) > AttackDistance and IsInAttackSight(MyID,MyEnemy) == false then -- 敵が攻撃射程外に出たとき MyState = CHASE_ST -- 追跡状態へ MyDestX, MyDestY = GetV (V_POSITION,MyEnemy) MoveToNear(MyEnemy,AttackDistance) --TraceAI ("ATTACK_ST -> CHASE_ST : ENEMY_OUTATTACKSIGHT_IN") return end if OnBattleTime == 0 then OnBattleTime = GetTick() -- 戦闘時間計測開始 end if BonusMode and OnBattleTime > 0 then -- 共闘モードの際 if GetTick() - OnBattleTime > BonusTime then return -- 何もしない end end if AdjustASPD then if (GetTick() - LastAttack) > Aspd then Attack (MyID,MyEnemy) -- 攻撃 LastAttack = GetTick() AttackedFlag = true -- 攻撃完了 end else Attack(MyID,MyEnemy) -- 攻撃 AttackedFlag = true -- 攻撃完了 end -- +++ 戦闘時間記録によるAS確率調整 +++ -- local id = GetV(V_HOMUNTYPE,MyEnemy) if MyEnemy < 100000 and Mob[id][M_RECORD] 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,MyOwner) if not Silencer then -- サイレンサー if o_motion ~= MOTION_SIT then -- 主人が座ってたらスキルは使わない UseAutoSkill() end end else SkillObject (MyID,MySkillLevel,MySkill,MyEnemy) AttackSkillDelay = GetTick() + SkillDelay -- 使用後ディレイ設定(2.5秒くらい MySkill = 0 end -- +++ 自動友達登録 +++ -- 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) == MyEnemy then -- 友達かもしれない人のターゲットが自分と同じとき SameTargetCount = SameTargetCount + 1 --TraceAI("** count : "..SameTargetCount) else -- そうじゃなかったら Neighbor = 0 -- 勘違いだったってことで SameTargetCount = 0 end if SameTargetCount >= TamedDegree then -- 20回くらい被ってたら SameTargetCount = 0 AddFriend(Neighbor) -- 友達と認識 end end end end ------------------------------------------------------------------------- -- 平均戦闘時間を記録する ------------------------------------------------------------------------- function RecordDist(enemy) local enemy = GetV(V_HOMUNTYPE,enemy) if enemy ~= -1 then if KillCount[enemy] == nil then -- 今日初めて戦う相手なら KillCount[enemy] = 1 DistTime[enemy] = GetTick() - OnBattleTime else KillCount[enemy] = KillCount[enemy] + 1 DistTime[enemy] = DistTime[enemy] + (GetTick() - OnBattleTime) end TraceAI(" -- Dist : "..enemy.." KillCount :"..KillCount[enemy].." TotalTime : "..DistTime[enemy]) local kc = KillCount[enemy] if kc - math.floor(kc/RecCount)*RecCount == 0 then -- 剰余計算.10回ごとに集計 if Mob[enemy][M_RECORD] == 0 or Mob[enemy][M_RECORD] == nil 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..".ini" local oldfilename = SaveDir.."BattleRecord.dat" local fp = io.open(filename,"r") -- ファイルを開く local index = 1000 -- MobのIDは1000から if (fp ~= nil) then while index < 3000 do index = index + 1 local line = fp:read() local p = 0 for n in string.gfind(line, "%d+") do p = p + 1 Mob[index][p] = tonumber(n) end end TraceAI(" ! ReadFile : "..filename.." index : "..index) fp:close() else -- ファイルが無い場合は新規作成 TraceAI(" NewFile "..filename) if BR_Filename then oldfilename = SaveDir..BR_Filename..".dat" end 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 rec = fp:read() if rec then Mob[index][M_RECORD] = tonumber(rec) else Mob[index][M_RECORD] = 0 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 end ---------------------------------------------------------- -- レコードをセーブする ---------------------------------------------------------- function SaveRecord(file,id) local filename = SaveDir..file..".ini" local fp fp = io.open(filename,"w") if (fp ~= nil) then local index = 1000 -- MobIDは1001から while index < 3000 do index = index + 1 local v1 = Mob[index][M_ACT] local v2 = Mob[index][M_SKILL] local v3 = Mob[index][M_AS] local v4 = Mob[index][M_CAST] local v5 = Mob[index][M_BOOST] local v6 = Mob[index][M_RANK] local v7 = Mob[index][M_RECORD] if v1==nil or v2==nil or v3==nil or v4==nil or v5==nil or v6 ==nil then --何かnilってたら初期化 v1,v2,v3,v4,v5,v6 = 3,6,0,0,0,0 end if v7==nil then v7 = 0 end fp:write(string.format("%d %d %d %d %d %d %d \n",v1,v2,v3,v4,v5,v6,v7)) -- 書き込み end fp:close() TraceAI(" ! Save Record : "..filename.." until line : "..index) 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 end end ------------------------------------------- -- Alt+Tで休息状態に居るとき ------------------------------------------- function OnFOLLOW_CMD_ST () TraceAI ("OnFOLLOW_CMD_ST") local d = GetDistance2(MyID,MyOwner) if ( d <= FollowDistance) then -- 指定セル以下の距離なら return end if (GetV(V_MOTION,MyID) == MOTION_STAND or GetV(V_MOTION,MyID) == MOTION_MOVE) then -- 自分のモーションが立ちか移動のとき DifferentialCount = DifferentialCount + 1 if DifferentialAccuracy + DifferentialCount < GetTick() and StayFlag==false then DifferentialCount = GetTick() MoveToNear (MyOwner, ChaceDistance) -- 主人に追従する TraceAI ("FOLLOW_ST -> FOLLOW_ST") return end end end ------------------------------------------------------------------------------------- -- 対象がプレイヤーであるかどうかを確かめる関数 ------------------------------------------------------------------------------------- function IsPlayer (id) if (id < 100000) then -- id 100000以下はMob return 0 end local Ty = GetV(V_HOMUNTYPE,id) if (0 <= Ty and Ty <= 25) then return 1 elseif (4001 <= Ty and Ty <= 4051) then return 1 else return 0 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 id ==-1 or IsPlayer(id) == 1 or IsHomunculus(id) == 1 then -- プレイヤーまたはホムのIDが入ってきたら return 0 -- 0を返して抜ける end if id < 100000 then id = GetV(V_HOMUNTYPE,id) -- 種別ID取得 if id ~= 0 and id ~= -1 and Mob[id][case] ~=nil then -- その配列の値が存在するかチェック if case == M_ACT then -- Mobに対する反応に関する判定 if Mob[id][case] == ACT_IGNORE then -- 無視する敵は0を返す return 0 elseif Mob[id][case] == ACT_NO then -- 一切攻撃しない敵は-1を返す return -1 elseif Mob[id][case] == ACT_RAID then -- 先制攻撃する敵には2を返す return 2 elseif Mob[id][case] == ACT_ESCAPE then -- 逃げる敵には-2を返す return -2 else -- どれも当てはまらない場合は1を return 1 end elseif case == M_SKILL then -- スキルの使用に関する判定 if Mob[id][case] == SKILL_NO then -- スキルを使わない場合1を返す return 1 else -- 使う場合は0を返す return 0 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 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 ------------------------------------------------------------------------------------- -- 対象が登録済みの念属性Mobであるかどうかを確かめる関数 -- 念Mobである→1が返る 念Mobでない→0が返る ------------------------------------------------------------------------------------- function IsGhost(id) id = GetV(V_HOMUNTYPE,id) for index, GhostID in ipairs(GhostMob) do if GhostID == id then -- idがGhostMobの中に存在すれば,1を返す return 1 end end return 0 -- 無ければ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 ) if target == 0 or ( target == myid or target == GetV( V_OWNER, myid ) or IsFriend(target) == 1) then ans = true elseif IsInSight(target) == 0 then --idの ターゲットが画面外ならtrue ans = true else return false end if OnActive then -- アクティブ索敵中のみ -- ターゲット周囲のプレイヤーチェック local players = GetActors() players = SelectActors(SELECT_IN_RANGE, 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 == 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 (HomunculusHP/HomunculusMaxHP)*100 > ActiveHP 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 -- 制限した距離以上なら range = math.floor(range*(dist/RangeDecreaseRate)) -- 索敵距離計算 else -- 制限した距離内だと range = -1 -- 索敵しないことに end end return range end ------------------------------------------------------------------------------------------------------------------------------------------- -- GetEnemy郡 ------------------------------------------------------------------------------------------------------------------------------------------- --インターフェース(モード指定, 選択元テーブル[オプション]) function SelectActors(argSelectMode, argActors, ...) local resultActors = {} if argSelectMode == SELECT_TARGET then --指定タゲで分類(オプション[ID]) resultActors = GetTargetActors(argActors, arg[1]) elseif argSelectMode == SELECT_MY_TARGET then --自分タゲで分類(オプションなし) resultActors = GetTargetActors(argActors, MyID) elseif argSelectMode == SELECT_OWNER_TARGET then --オーナータゲで分類(オプションなし) resultActors = GetTargetActors(argActors, GetV(V_OWNER, MyID)) elseif argSelectMode == SELECT_FREE_TARGET then --ターゲット0で分類(オプションなし) resultActors = GetTargetActors(argActors, 0) elseif argSelectMode == SELECT_MOTION then --指定モーションで分類(オプション[モーション定数の羅列]) resultActors = GetMotionActors(argActors, arg) elseif argSelectMode == SELECT_NOT_MOTION then --指定モーションで分類(オプション[モーション定数の羅列]) resultActors = GetNotMotionActors(argActors, arg) elseif argSelectMode == SELECT_ENEMYS then --IsMonsterで分類(オプションなし) resultActors = GetEnemys(argActors) elseif argSelectMode == SELECT_IN_RANGE then --指定距離内で分類(オプション[ID, 範囲]) resultActors = GetInRangeActors(argActors, arg[1], arg[2]) elseif argSelectMode == SELECT_OUT_RANGE then --指定距離外で分類(オプション[ID, 範囲]) resultActors = GetOutRangeActors(argActors, arg[1], arg[2]) elseif argSelectMode == SELECT_PLAYERS then --IsPlayersで分類(オプションなし) resultActors = GetPlayers(argActors) else TraceAI("ErrorSelectMode") end return resultActors 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 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) == -2 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) ~= -1 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) > 3 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) ~= -1 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 = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 actors = SelectActors(SELECT_IN_RANGE, 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 = SelectActors(SELECT_OWNER_TARGET, actors) --↑のうちターゲットが主人 actors = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_IN_RANGE, actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --自分を攻撃中の中から一番近い敵を探す function GetMyEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = SelectActors(SELECT_MY_TARGET, actors) --↑のうちターゲットが自分 actors = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 actors = SelectActors(SELECT_IN_RANGE, 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 = SelectActors(SELECT_FREE_TARGET, actors) --↑のうちターゲット0 if HalfActive and ActiveFlag==false then -- ノンアクで先制設定があるとき actors = GetRaidEnemys(actors) -- ↑のうち先制リストのMob actors = SelectActors(SELECT_IN_RANGE, actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 else -- そうでなければ actors = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_IN_RANGE, actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD, MOTION_ATTACK, MOTION_ATTACK2) --↑のうち死&攻撃モーション以外 prior = GetEnemyByRecord(actors,3) --↑のうち優先度が高い敵 if not prior[1] then actors = GetEnemyByRecord(actors,rank) --↑のうち一番弱い else actors = prior end end resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い -- 他人とターゲットの距離チェック if resultEnemyID ~= 0 then if not IsNotNoManner(MyID,resultEnemyID) then resultEnemyID = 0 end else OnActive = false -- 何も居なかったらアクティブ索敵終了 end return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --主人を遠距離攻撃中の中から一番近い敵を探す function GetOwnerOutRangeEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = SelectActors(SELECT_OWNER_TARGET, actors) --↑のうちターゲットが主人 actors = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_OUT_RANGE, actors, MyOwner, 4) --↑のうちオーナーから4セル以上 actors = SelectActors(SELECT_IN_RANGE, actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- --自分を遠距離攻撃中の中から一番近い敵を探す function GetOutRangeEnemyID(dist) local actors = {} local resultEnemyID = 0 actors = GetActors() --画面内の全員 actors = SelectActors(SELECT_MY_TARGET, actors) --↑のうちターゲットが自分 actors = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_OUT_RANGE, actors, MyOwner, 4) --↑のうち自分から4セル以上 actors = SelectActors(SELECT_IN_RANGE, actors, MyOwner, dist) --↑のうちオーナーから指定セル以内 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 resultEnemyID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultEnemyID end ------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------- -- 指定セル内の友達ではないプレイヤーキャラクターを返す function GetPlayersNearID(dist) local actors = {} local resultPlayerID = 0 actors = GetActors() -- 画面内の全員 actors = SelectActors(SELECT_PLAYERS,actors) -- ↑のうち主人以外のPC actors = SelectActors(SELECT_IN_RANGE, actors, MyOwner, dist) --↑のうち自分からdistセル以内 actors = GetNotFriend(actors) -- ↑のうち友達ではないPC actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 resultPlayerID = GetNearActorID(MyID, actors) --↑のうち自分に一番近い return resultPlayerID end ------------------------------------------------------------------------------------------------------------------------------------------- -- 友達ではなく、同じターゲットを持つ一番近いプレイヤーキャラクターを返す function GetNeighborID() local actors = {} local resultPlayerID = 0 actors = GetActors() -- 画面内の全員 actors = SelectActors(SELECT_PLAYERS,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 record = Mob[id][M_RECORD] -- 記録読み込み elseif case == 3 then record = Mob[id][M_RANK] -- 記録読み込み end if record and record ~= 0 then -- 記録が空または0じゃなければ local sub = math.abs(record - value) if sub < min_value then min_value = sub resultID = id -- 該当するMob種ID end end end if resultID == GetV(V_HOMUNTYPE,GetV(V_TARGET,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 = SelectActors(SELECT_ENEMYS, actors) --↑のうち攻撃可能 actors = SelectActors(SELECT_NOT_MOTION, actors, MOTION_DEAD) --↑のうち死にモーション以外 actors = SelectActors(SELECT_IN_RANGE, 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 ") 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, ChaceDistance) end else OnFOLLOW_ST() -- 敵が居ない時はFOLLOW_STと同じ動作 end end -- 回復したら通常行動に戻る if ((HomunculusHP/HomunculusMaxHP)*100 >= HomunculusSafetyHP) then MyState = IDLE_ST NowRondo = false 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 MyType == LIF or MyType == LIF_H or MyType == LIF2 or MyType == LIF_H2 then -- リーフなら if AutoSkillSPLimit < (HomunculusSP / HomunculusMaxSP)*100 and HomunculusSP ~= 0 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 = math.abs(dirX) local ay = math.abs(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 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 ActiveFlag = false -- ノンアクティブにして 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 --TraceAI (" ! LostEnemy : "..LostEnemy) ResetEnemy() -- 敵情報をリセット TraceAI ("ATTACK_ST -> IDLE_ST") return end if GetTick() > ShootDelay then -- ディレイ間隔2秒経過していたら MySkillLevel,MySkill = SetActiveSkill(ShootLevel) if MySkillLevel <= 0 then MySkillLevel = 1 end SkillObject (MyID,MySkillLevel,MySkill,MyEnemy) -- スキル発動 ShootDelay = GetTick() + SkillDelay end target = GetMyEnemyID(OnBattleDistance) -- 自分を攻撃する敵を探す if target ~= 0 then if IsInAttackSight(MyID,target) and GetDistance2(MyID,target) < 3 and GetTick() > MoveDelay then -- 接敵されたら1秒間隔で逃げる SetRondo(PatrolPattern,PatrolRange) Rondo(PatrolPattern,PatrolRange) end end end 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 IsPlayer(id) == 1 and id ~= GetV(V_OWNER,MyID) then -- IDが主人以外のプレイヤー if GvMode then AddGuildMember(id) -- Gvモードならギルメンリストへ else AddFriend(id) -- 友達リストに追加 end elseif IsHomunculus(id) == 1 and id ~= MyID then -- IDが自分以外のホムンクルス AddFriend(id) -- 友達リストに追加 elseif id == 0 then if GvMode then -- Gvモードのとき SetSnipeArea(dx,dy) -- エリア設定 end end elseif (msg[1] == ATTACK_OBJECT_CMD) then -- 攻撃命令コマンドの場合は無視する敵登録動作 id = msg[2] -- 攻撃対象のID取得 dx,dy = GetV(V_POSITION,id) -- 対象の座標取得 if IsMonster(id) == 1 then if GetV(V_MOTION,MyOwner) == MOTION_SIT then -- 主人が座ってるときなら AddIgnore(id) -- 無視リストに追加 else -- それ以外なら AddRaid(id) -- 先制リストに追加 end end end end ------------------------------------------------------------------------- -- リストに追加する -- id: 追加するID argList: 追加先のリスト index; 追加先リストのインデックス ------------------------------------------------------------------------- function AddList(id,argList,index) index = index + 1 argList[index] = id return argList,index end ------------------------------------------------------------------------- -- リストから削除する -- id: 削除するID argList: 削除元のリスト index; 削除元リストのインデックス ------------------------------------------------------------------------- function DelList(id,argList,index) local delnum for i, searchedID in ipairs(argList) do if searchedID == id then -- 削除対象のIDを発見 delnum = i if i ~= index then -- これがリストの最後じゃなかったら RemoveFlag = true else argList[index] = nil -- 最後尾のIDをnilに end index = index - 1 end if RemoveFlag then argList[i] = argList[i+1] if i == index+1 then argList[index+1] = nil -- 最後尾のIDをnilに RemoveFlag = nil end end end return argList,index end ------------------------------------------------------------------------- -- 友達追加・削除 ------------------------------------------------------------------------- function AddFriend (id) if IsFriend(id) == 0 then -- idが友達じゃなかったら追加 if IsPlayer(id) == 1 then -- プレイヤーなら FriendList[id] = GetV(V_HOMUNTYPE,id) SaveList("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 else -- 既に友達だったら削除 if IsPlayer(id) == 1 then FriendList[id] = nil SaveList("Friend",FriendList) -- 保存 elseif IsHomunculus(id) == 1 then HomFriendList[id] = nil -- ホム友達リストから削除 end FriendIndex = TableSize(FriendList) + TableSize(HomFriendList) TraceAI(" ** Delete Friend! : " ..id.." LestMem : "..FriendIndex) Response(0) -- 削除レスポンス end end ------------------------------------------------------------------------- -- 無視する敵追加・削除 ------------------------------------------------------------------------- function AddIgnore (id) local index = GetV(V_HOMUNTYPE,id) -- 種族IDを取得 if IsMob(M_ACT,id) == 1 then -- idが既に登録されてなかったら追加 Mob[index][M_ACT] = ACT_IGNORE Mob[index][M_RANK] = 0 -- 優先度設定を初期化 TraceAI(" // Ignore Mob! : " ..index) Response(1) elseif IsMob(M_ACT,id) == 0 then -- 既に登録されてたら Mob[index][M_ACT] = ACT_NO -- 完全無視敵 TraceAI(" // NoRegard Mob! : " ..index) Response(2) else -- それ以外は通常判定に Mob[index][M_ACT] = ACT_NORMAL TraceAI(" // Normal Mob! : " ..index) Response(0) end SaveRecord(MobFilename) 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) Response(1) else -- 既に登録されてたら削除 Mob[index][M_ACT] = ACT_NORMAL TraceAI(" // Normal Mob! : " ..index) Response(0) end SaveRecord(MobFilename) 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) 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) end ------------------------------------------------------------------------- -- アラートパフォーマンス ------------------------------------------------------------------------- function OnALERT_ST() TraceAI("OnALERT_ST") if MoveDelay < GetTick() then Rondo(9,1) -- 主人の周囲を旋回して通知 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) -- Comp end end ------------------------------------------------------------------------- -- 寝落ち防止用 ------------------------------------------------------------------------- function ExitEvent() do local timestamp = os.time() local ownerMotion = GetV(V_MOTION,MyOwner) ExitEvent = function() local ownerMotion2 = GetV(V_MOTION,MyOwner) -- 主人のモーションが同じ時間 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 (HomunculusSP/HomunculusMaxSP)*100 < 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 = HomunculusSP --TraceAI(" -+- Wait SPR SP:"..HomunculusSP) elseif pass == 4 then if HomunculusSP > PreSP then SPR_Flag = true StayTime = os.time() end else SPR_Flag = false if pass > 4 then StayTime = os.time() --TraceAI(" -+- Get SPR SP:"..HomunculusSP) end end else SPR_Flag = false MyPosX = x MyPosY = y StayTime = os.time() end else SPR_Flag = 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 -- アラート状態にして抜ける return end else MentalChange = false end end end ------------------------------------------------------------------------- -- 沈黙状態チェック用関数 ------------------------------------------------------------------------- function CheckSilence() if UseSkillFlag then -- スキル使用していたら local nowSP = GetV (V_SP,MyID) if HomunculusSP > nowSP then -- SPが前回より減ってたら SkillFailedCount = 0 -- 何事もなく Silencer = false else if SkillFailedCount > 20 then -- 失敗カウント20以上あったら Silencer = true -- サイレンサーON SilenceTime = GetTick() + ST_BaseTime*((100-HomVIT)/100) -- 沈黙終了予想時間 SkillFailedCount = 0 -- カウントリセット TraceAI(" !! Silenced ") else SkillFailedCount = SkillFailedCount + 1 end end UseSkillFlag = false else -- スキル使用していない時 if Silencer then -- サイレンサーONだったら if SilenceTime < GetTick() then -- 予想終了時間を経過していたら Silencer = false -- サイレンサーOFF TraceAI(" -->> Recover Silence ") end 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 >= 1000 and pass < 1500 then -- 1秒以上1.5秒未満 case = 1 -- 解消ケース1 elseif pass >= 1500 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 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 MyState = LastState end elseif case == 2 then if mx ~= PrevPos2x or my ~= PrevPos2y then Move(MyID,PrevPos2x,PrevPos2y) end StandTime = GetTick() TraceAI("-->> Stack ?!") end end else StandTime = GetTick() -- それ以外の状態は無関係なので時間だけ更新 end -- 攻撃のスタックチェック if AttackFalseCheck or SBR then -- SBRの念チェックも同じとこでやっちゃう if MyState == ATTACK_ST and not NowAwaying then if MyEnemy ~= GetV(V_TARGET,MyID) then -- ターゲットした敵と実際のターゲットの違いを監視 AttackFalseCount = AttackFalseCount + 1 if AttackFalseCount > 4 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 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 > 1500 then -- ダメージモーション以外のモーションが1.5秒以上確認されたら if not SBR then -- SBRじゃないなら if pass > 2000 then -- ダメージモーション以外のモーションが2秒以上確認されたら SkipStack = true AttackFalseTime = GetTick() -- 時間更新 TraceAI("-->> Stack on Attack ?") end else if id == 1131 then -- 野生のジョーカー限定で local g_enemy = MyEnemy ChangeGhost = true ResetEnemy() -- 敵ID以外の情報リセット MyEnemy = g_enemy end 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) -- 敵の方向へ一歩進んでみる ReturnFlag = true SkipStack = false end else AttackFalseTime = GetTick() -- それ以外の状態は無関係なので時間だけ更新 end end end ------------------------------------------------------ -- 自律行動時のメインルーチン ------------------------------------------------------ function AI(myid) MyID = myid ReturnFlag = false if InitialFlag then Initialize() end if SleepFlag then if InitializedTime + SleepTime < GetTick() then -- 起動後1秒経ったら SleepFlag = false -- スリープ終わり end return -- 起動から1秒間は何もしない end if AttackedFlag and not AdjustASPD and MultiAttack >= 3 then -- 攻撃フラグ立ってたら Attack(MyID,MyEnemy) -- もう1回攻撃しとく AttackedFlag = false end 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 if GvMode then -- Gvモードなら GvAI() -- GvAIへ return end local motion = GetV(V_MOTION,MyOwner) if AutoCheck_Silence and motion ~= MOTION_SIT then -- 沈黙自動チェックONかつ主人が座り以外 CheckSilence() end -- HP/SP等の取得 HomunculusHP = GetV (V_HP,MyID) HomunculusMaxHP = GetV (V_MAXHP,MyID) HomunculusSP = GetV (V_SP,MyID) HomunculusMaxSP = GetV (V_MAXSP,MyID) OwnerHP = GetV (V_HP,MyOwner) OwnerMaxHP = GetV (V_MAXHP,MyOwner) local ox,oy = GetV(V_POSITION,MyOwner) SaveLastWalk (ox,oy) -- 主人の足跡監視 if WaitSPR then WaitUntilSPR() -- SPR待ち end CheckStack() -- 移動詰まり解消 if AutoRecoverSkill and AutoSkillSPLimit < (HomunculusSP / HomunculusMaxSP)*100 then -- オートリカバーONかつSPが一定割合以上 if (OwnerHP/OwnerMaxHP)*100 < 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 ((HomunculusHP/HomunculusMaxHP)*100 < HomunculusSafetyHP) then -- HPが残り少ない場合 if EscapeFlag and MyState~=MOVE_CMD_ST then ResetEnemy() MyState = ESCAPE_ST elseif not EscapeFlag and EmergencyBoost and MyState == ATTACK_ST then AutoSkill = ASP_Emergency end end MC_AlertCheck() -- MChアラート if MyState ~= IDLE_ST then -- 旋回動作用調整 RondEnd = os.time() if MyState ~= ALERT_ST then PatrolVector = 0 end end if ReturnFlag then -- 状態処理前に一旦ループを終わらせたい場合 TraceAI(" ====------->>> exit") return 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 () 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 () end if (MyState == ATTACK_OBJECT_CMD_ST) then OnATTACK_OBJECT_CMD_ST () end if AttackedFlag and not AdjustASPD and MultiAttack >= 2 then -- 攻撃フラグ立ってたら if GetV(V_MOTION,MyEnemy) ~= MOTION_DEAD then Attack(MyID,MyEnemy) -- もう1回攻撃しとく if MultiAttack == 2 then AttackedFlag = false end else AttackedFlag = false end elseif MultiAttack == 1 then AttackedFlag = false end if AutoExit and GetTick() - ExitCheckTime > 2000 then -- 寝落ちチェック ExitEvent() end end