-- "Doki^2 AI" Type-Normal 2006/08/23u -- Arranged by F.Kogarashi(0-MATERIAL) require "./AI/Const.lua" require "./AI/Util.lua" ------------------------------------------ -- state ------------------------------------------ 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 ------------------------------------------ ------------------------------------------ -- global variable ------------------------------------------ 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 -- ホムンクルスのスキルレベル ------------------------------------------ ------------------------------------------ -- どきどき版追加 ------------------------------------------ HomunculusSafetyHP = 20 -- 単位%:ホムのHPがこれ以下の場合は攻撃参加しない HomunculusDeadlineHP = 0 -- 単位%:ホムのHPがこれ以下の場合は自動ヒール(リーフのみ) ※現在無効 OwnerDeadlineHP = 30 -- 単位%:主人の残りHPがこれ以下の場合は自動ヒール(リーフのみ) PatrolRange = 3 -- くるくる半径/この距離以上開くと主人の元へ追尾 PatrolVector = 0 -- ★変更しないこと! AutoSkill = 10 -- ホムの自動スキル最低発動確率%(+残りSPにより変動) AutoSkillSPLimit = 20 -- ホムのSPがこの%を下回った場合には自動スキル発動なし WaitClock = 0 -- ★変更しないこと! PatrolSpeed = 350 -- くるくる移動速度(ミリ秒) PatrolAngle = 0.80 -- くるくる回転角(菱形:0.50 円形:0.60〜0.80 方形:1) PatrolRest = 90 -- ホムの残りSPがこの%以下の場合はくるくる停止(回復のため) -- 逆に言えばこの%以上に回復したらくるくる回転開始 NextInterval = 0 -- ★変更しないこと! PatrolInterval = 7000 -- 一回転してから次に回転するまでの間隔 HomunculusHeal = 1 -- リーフの場合、自動ヒールを使用(0:off 1:on) HomunculusEscape = 1 -- ホムHP低下時に戦闘中断→逃亡(0:off 1:on) EscapeRange = 2 -- ホム危険時、主人の何マス後ろへ隠れるか -- ↑IdleType=2,3の時の追尾距離設定も兼ねています(↓) IdleType = 3 -- アイドル時の行動パターン -- (0:何もしない 1:くるくる回転 2:常に背後[DQ風] 3:DQ→くるくる) HomunculusPatience = 10000 -- ホムンクルスの忍耐力 --(IdleType=3の時、このミリ秒間主人が動かないでいると勝手に回り出す) PatrolType = 0 -- 回転形状(0:時計回り 1:反時計回り 2:十字(ENWS) 3:十字(EWNS)) --      (4:ジグザグ 5:8の字運動 9:範囲内をランダム移動) -- ※4と5にする場合は、PatrolSpeedを通常より大きくしたほうが吉 HomunculusLoyalty = 1 -- 忠誠度(0:交戦中の敵に集中 1:主人と同じ敵を攻撃) HomunculusDevotion = 1 -- 献身度(1:デフォルト -1:主人の前方で待機しホム自身が盾となる) AutoSkillOnce = 1 -- オートスキルの発動回数を一度だけに制限(0:off 1:on) LastOwnerX = 0 -- ★変更しないこと! LastOwnerY = 0 -- ★変更しないこと! ESCAPE_ST = 255 -- ★変更しないこと! EscapeSteps = 0 -- ★変更しないこと! EscapeEastWest = 0 -- ★変更しないこと! EscapeNorthSouth = 0 -- ★変更しないこと! LastMoveTime = 0 -- ★変更しないこと! HomunculusSPLast = 99999 -- ★変更しないこと! HomunculusHealDelay = 0 -- ★変更しないこと! ActionFlag = 0 -- ★変更しないこと! LastSkillEnemy = 0 -- ★変更しないこと! ------------------------------------------ ------------- どきどき版追加command --------------------- function MoveToOwner (id) -- 重力仕様のMoveToOwnerは処理遅いんで軽量化 local x,y = GetV (V_POSITION,GetV(V_OWNER,id)) Move (id,x+1,y) -- 主人と重ならないためにx+1 end function RollingAround () -- 主人を中心にくるくる回転 PatrolVector = PatrolVector +1 -- くるくる方向++ if (PatrolType==0) then -- 時計回りに回転 if (PatrolVector == 1) then -- switch文がないとはなんたるちあ! Move (MyID,OwnerX-math.ceil(PatrolRange*PatrolAngle),OwnerY-math.ceil(PatrolRange*PatrolAngle)) --2 elseif (PatrolVector == 2) then Move (MyID,OwnerX-PatrolRange,OwnerY) --3 elseif (PatrolVector == 3) then Move (MyID,OwnerX-math.ceil(PatrolRange*PatrolAngle),OwnerY+math.ceil(PatrolRange*PatrolAngle)) --4 elseif (PatrolVector == 4) then Move (MyID,OwnerX,OwnerY+PatrolRange) --5 elseif (PatrolVector == 5) then Move (MyID,OwnerX+math.ceil(PatrolRange*PatrolAngle),OwnerY+math.ceil(PatrolRange*PatrolAngle)) --6 elseif (PatrolVector == 6) then Move (MyID,OwnerX+PatrolRange,OwnerY) --7 elseif (PatrolVector == 7) then Move (MyID,OwnerX+math.ceil(PatrolRange*PatrolAngle),OwnerY-math.ceil(PatrolRange*PatrolAngle)) --8 elseif (PatrolVector == 8) then Move (MyID,OwnerX,OwnerY-PatrolRange) --1 PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end elseif (PatrolType==1) then -- 反時計回りに回転 if (PatrolVector == 1) then -- switch文がないとはなんたるちあ! Move (MyID,OwnerX+math.ceil(PatrolRange*PatrolAngle),OwnerY-math.ceil(PatrolRange*PatrolAngle)) --8 elseif (PatrolVector == 2) then Move (MyID,OwnerX+PatrolRange,OwnerY) --7 elseif (PatrolVector == 3) then Move (MyID,OwnerX+math.ceil(PatrolRange*PatrolAngle),OwnerY+math.ceil(PatrolRange*PatrolAngle)) --6 elseif (PatrolVector == 4) then Move (MyID,OwnerX,OwnerY+PatrolRange) --5 elseif (PatrolVector == 5) then Move (MyID,OwnerX-math.ceil(PatrolRange*PatrolAngle),OwnerY+math.ceil(PatrolRange*PatrolAngle)) --4 elseif (PatrolVector == 6) then Move (MyID,OwnerX-PatrolRange,OwnerY) --3 elseif (PatrolVector == 7) then Move (MyID,OwnerX-math.ceil(PatrolRange*PatrolAngle),OwnerY-math.ceil(PatrolRange*PatrolAngle)) --2 elseif (PatrolVector == 8) then Move (MyID,OwnerX,OwnerY-PatrolRange) --1 PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end elseif (PatrolType==2) then -- 十字(1) if (PatrolVector == 1) then Move (MyID,OwnerX+PatrolRange,OwnerY) elseif (PatrolVector == 2) then Move (MyID,OwnerX+1,OwnerY) elseif (PatrolVector == 3) then Move (MyID,OwnerX,OwnerY+PatrolRange) elseif (PatrolVector == 4) then Move (MyID,OwnerX,OwnerY+1) elseif (PatrolVector == 5) then Move (MyID,OwnerX-PatrolRange,OwnerY) elseif (PatrolVector == 6) then Move (MyID,OwnerX-1,OwnerY) elseif (PatrolVector == 7) then Move (MyID,OwnerX,OwnerY-PatrolRange) elseif (PatrolVector == 8) then Move (MyID,OwnerX,OwnerY-1) PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end elseif (PatrolType==3) then -- 十字(2) if (PatrolVector == 1) then Move (MyID,OwnerX+PatrolRange,OwnerY) elseif (PatrolVector == 2) then Move (MyID,OwnerX+1,OwnerY) elseif (PatrolVector == 3) then Move (MyID,OwnerX-PatrolRange,OwnerY) elseif (PatrolVector == 4) then Move (MyID,OwnerX-1,OwnerY) elseif (PatrolVector == 5) then Move (MyID,OwnerX,OwnerY+PatrolRange) elseif (PatrolVector == 6) then Move (MyID,OwnerX,OwnerY+1) elseif (PatrolVector == 7) then Move (MyID,OwnerX,OwnerY-PatrolRange) elseif (PatrolVector == 8) then Move (MyID,OwnerX,OwnerY-1) PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end elseif (PatrolType==4) then -- ジグザグ回転 if (PatrolVector == 1) then Move (MyID,OwnerX-math.ceil(PatrolRange*PatrolAngle),OwnerY-math.ceil(PatrolRange*PatrolAngle)) elseif (PatrolVector == 2) then Move (MyID,OwnerX,OwnerY+PatrolRange) elseif (PatrolVector == 3) then Move (MyID,OwnerX+math.ceil(PatrolRange*PatrolAngle),OwnerY-math.ceil(PatrolRange*PatrolAngle)) elseif (PatrolVector == 4) then Move (MyID,OwnerX-PatrolRange,OwnerY) elseif (PatrolVector == 5) then Move (MyID,OwnerX+math.ceil(PatrolRange*PatrolAngle),OwnerY+math.ceil(PatrolRange*PatrolAngle)) elseif (PatrolVector == 6) then Move (MyID,OwnerX,OwnerY-PatrolRange) elseif (PatrolVector == 7) then Move (MyID,OwnerX-math.ceil(PatrolRange*PatrolAngle),OwnerY+math.ceil(PatrolRange*PatrolAngle)) elseif (PatrolVector == 8) then Move (MyID,OwnerX+PatrolRange,OwnerY) PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end elseif (PatrolType==5) then -- 8の字回転 if (PatrolVector == 1) then Move (MyID,OwnerX-PatrolRange,OwnerY+math.ceil(PatrolRange*PatrolAngle)) elseif (PatrolVector == 2) then Move (MyID,OwnerX,OwnerY+PatrolRange) elseif (PatrolVector == 3) then Move (MyID,OwnerX+PatrolRange,OwnerY+math.ceil(PatrolRange*PatrolAngle)) elseif (PatrolVector == 4) then Move (MyID,OwnerX+1,OwnerY+1) elseif (PatrolVector == 5) then Move (MyID,OwnerX-PatrolRange,OwnerY-math.floor(PatrolRange*PatrolAngle)) elseif (PatrolVector == 6) then Move (MyID,OwnerX,OwnerY-PatrolRange) elseif (PatrolVector == 7) then Move (MyID,OwnerX+PatrolRange,OwnerY-math.floor(PatrolRange*PatrolAngle)) elseif (PatrolVector == 8) then Move (MyID,OwnerX-1,OwnerY+1) PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end else -- 範囲内を完全ランダム Move (MyID,OwnerX+math.random((-1)*PatrolRange,PatrolRange),OwnerY+math.random((-1)*PatrolRange,PatrolRange)) PatrolVector = 0 NextInterval = GetTick() + PatrolInterval end WaitClock = GetTick() + PatrolSpeed end function MoveBackward (code) -- ホムを主人の後方に移動させる -- 隠れる方角を計算し直し EscapeEastWest = 0 EscapeNorthSouth = 0 -- バグ回避のため献身度は1か-1以外受け付けない if (HomunculusDevotion~=1 and HomunculusDevotion~=-1) then HomunculusDevotion = 1 end if (code==1) then -- ホムの座標を取得 local x,y = GetV (V_POSITION,MyID) -- マップ左下が0,0、右上がMAX,MAX -- 主人からの方角を調べ、その180度反対方向へ if (OwnerX - x > 0) then EscapeEastWest = 1 * EscapeRange * HomunculusDevotion elseif (OwnerX - x < 0) then EscapeEastWest = -1 * EscapeRange * HomunculusDevotion end if (OwnerY - y > 0) then EscapeNorthSouth = 1 * EscapeRange * HomunculusDevotion elseif (OwnerY - y < 0) then EscapeNorthSouth = -1 * EscapeRange * HomunculusDevotion end -- 2回目以降は次のステップへ EscapeSteps = 2 else -- 主人の足跡を調べ、その後ろへ if (LastOwnerX - OwnerX > 0) then EscapeEastWest = 1 * EscapeRange * HomunculusDevotion elseif (LastOwnerX - OwnerX < 0) then EscapeEastWest = -1 * EscapeRange * HomunculusDevotion end if (LastOwnerY - OwnerY > 0) then EscapeNorthSouth = 1 * EscapeRange * HomunculusDevotion elseif (LastOwnerY - OwnerY < 0) then EscapeNorthSouth = -1 * EscapeRange * HomunculusDevotion end end -- 主人の背後に隠れる Move (MyID,OwnerX+EscapeEastWest,OwnerY+EscapeNorthSouth) -- 主人の足跡を記録 SaveLastWalk () end function SaveLastWalk () -- 主人の足跡を記録 LastOwnerX = OwnerX LastOwnerY = OwnerY LastMoveTime = GetTick() PatrolVector = 0 end function EMERGENCY_ESCAPE () -- 緊急離脱 -- 主人とホムの座標を取得 OwnerX, OwnerY = GetV (V_POSITION,GetV(V_OWNER,MyID)) local x,y = GetV (V_POSITION,MyID) if (EscapeSteps == 1) then -- 離脱発動時のみ MoveBackward (1) elseif (LastOwnerX~=OwnerX or LastOwnerY~=OwnerY) then -- 発動後に主人が移動したら MoveBackward (2) elseif (LastOwnerX==OwnerX or LastOwnerY==OwnerY) then -- ラグやノックバックで移動が完了してない場合があるので -- 退避完了していなければもう一度移動命令 if( x ~= OwnerX+EscapeEastWest or y ~= OwnerY+EscapeNorthSouth ) then Move (MyID,OwnerX+EscapeEastWest,OwnerY+EscapeNorthSouth) end end -- 主人が動かない限り、ホムはHP回復まで待機 local HomunculusHP = GetV (V_HP,MyID) local HomunculusMaxHP = GetV (V_MAXHP,MyID) -- 回復したら通常行動に戻る if ((HomunculusHP/HomunculusMaxHP)*100 >= HomunculusSafetyHP) then MyState = IDLE_ST EscapeSteps = 0 -- 主人の足跡をクリア LastOwnerX = 0 LastOwnerY = 0 -- 隠れる方角もクリア EscapeEastWest = 0 EscapeNorthSouth = 0 end end ------------- command process --------------------- function OnMOVE_CMD (x,y) TraceAI ("OnMOVE_CMD") 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) 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 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 OnPATROL_CMD (x,y) TraceAI ("OnPATROL_CMD") MyPatrolX , MyPatrolY = GetV (V_POSITION,MyID) MyDestX = x MyDestY = y Move (MyID,x,y) MyState = PATROL_CMD_ST end function OnHOLD_CMD () TraceAI ("OnHOLD_CMD") MyDestX = 0 MyDestY = 0 MyEnemy = 0 MyState = HOLD_CMD_ST end function OnSKILL_OBJECT_CMD (level,skill,id) TraceAI ("OnSKILL_OBJECT_CMD") MySkillLevel = level MySkill = skill MyEnemy = id MyState = CHASE_ST 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 MoveToOwner (MyID) MyState = FOLLOW_CMD_ST MyDestX, MyDestY = GetV (V_POSITION,GetV(V_OWNER,MyID)) MyEnemy = 0 MySkill = 0 TraceAI ("OnFOLLOW_CMD") else MyState = IDLE_ST MyEnemy = 0 MySkill = 0 TraceAI ("FOLLOW_CMD_ST --> IDLE_ST") end end function ProcessCommand (msg) if (msg[1] == MOVE_CMD) then OnMOVE_CMD (msg[2],msg[3]) TraceAI ("MOVE_CMD") elseif (msg[1] == 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 -------------- state process -------------------- function OnIDLE_ST () TraceAI ("OnIDLE_ST") local cmd = List.popleft(ResCmdList) if (cmd ~= nil) then ProcessCommand (cmd) -- 予約コマンド処理 return end -- 主人が戦闘状態か否かをスプライトパターンから判断 if (GetV(V_MOTION,GetV(V_OWNER,MyID)) == (MOTION_ATTACK or MOTION_CASTING or MOTION_SKILL) and IsMonster(GetV(V_TARGET,GetV(V_OWNER,MyID))) == 1) then MyState = CHASE_ST MyEnemy = GetV(V_TARGET,GetV(V_OWNER,MyID)) TraceAI ("IDLE_ST -> CHASE_ST : MYOWNER_IS_FIGHTING") return end local object = GetOwnerEnemy (MyID) if (object ~= 0) then -- MYOWNER_ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("IDLE_ST -> CHASE_ST : MYOWNER_ATTACKED_IN") return end object = GetMyEnemy (MyID) if (object ~= 0) then -- ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("IDLE_ST -> CHASE_ST : ATTACKED_IN") return end local distance = GetDistanceFromOwner(MyID) if ( distance > PatrolRange+1 or distance == -1) then -- MYOWNER_OUTSIGNT_IN MyState = FOLLOW_ST TraceAI ("IDLE_ST -> FOLLOW_ST") return else OwnerX, OwnerY = GetV (V_POSITION,GetV(V_OWNER,MyID)) -- 座標取得 if (IdleType==1) then -- ホムのSP状態を取得 local HomunculusSP = GetV (V_SP,MyID) local HomunculusMaxSP = GetV (V_MAXSP,MyID) -- タイマーが発動、かつホムの残りSPが一定%を越えた状態なら主人の周囲を回転 if (WaitClock < GetTick() and PatrolRest <= (HomunculusSP / HomunculusMaxSP)*100 and NextInterval < GetTick() and PatrolRange > 0) then RollingAround () end elseif (IdleType==2) then if (LastOwnerX ~= OwnerX or LastOwnerY ~= OwnerY) then MoveBackward (2) elseif (LastOwnerX == OwnerX or LastOwnerY == OwnerY) then -- ラグやノックバックで移動が完了してない場合があるので local x,y = GetV (V_POSITION,MyID) -- 退避完了していなければもう一度移動命令 if( x ~= OwnerX+EscapeEastWest or y ~= OwnerY+EscapeNorthSouth ) then Move (MyID,OwnerX+EscapeEastWest,OwnerY+EscapeNorthSouth) end end elseif (IdleType==3) then if (LastOwnerX ~= OwnerX or LastOwnerY ~= OwnerY) then MoveBackward (2) elseif (LastOwner == OwnerX or LastOwnerY == OwnerY) then if (LastMoveTime+HomunculusPatience < GetTick()) then -- ホムのSP状態を取得 local HomunculusSP = GetV (V_SP,MyID) local HomunculusMaxSP = GetV (V_MAXSP,MyID) -- タイマーが発動、かつホムの残りSPが一定%を越えた状態なら主人の周囲を回転 if (WaitClock < GetTick() and PatrolRest <= (HomunculusSP / HomunculusMaxSP)*100 and NextInterval < GetTick() and PatrolRange > 0) then RollingAround () end end end end end end function OnFOLLOW_ST () TraceAI ("OnFOLLOW_ST") if (GetDistanceFromOwner(MyID) <= PatrolRange) then -- DESTINATION_ARRIVED_IN MyState = IDLE_ST TraceAI ("FOLLOW_ST -> IDLW_ST") return elseif (GetV(V_MOTION,MyID) == MOTION_STAND or GetV(V_MOTION,MyID) == MOTION_MOVE) then MoveToOwner (MyID) TraceAI ("FOLLOW_ST -> FOLLOW_ST") return end end function OnCHASE_ST () TraceAI ("OnCHASE_ST") if (true == IsInAttackSight(MyID,MyEnemy)) then -- ENEMY_INATTACKSIGHT_IN MyState = ATTACK_ST TraceAI ("CHASE_ST -> ATTACK_ST : ENEMY_INATTACKSIGHT_IN") return end if (MOTION_DEAD == GetV(V_MOTION,MyEnemy)) then -- ENEMY_DEAD_IN MyState = IDLE_ST TraceAI ("CHASE_ST -> IDLE_ST") return end local distance = GetDistanceFromOwner(MyID) if ( distance > PatrolRange+1 or distance == -1) then -- MYOWNER_OUTSIGNT_IN MyState = FOLLOW_ST TraceAI ("CHASE_ST -> FOLLOW_ST") return end if (true == IsOutOfSight(MyID,MyEnemy)) then -- ENEMY_OUTSIGHT_IN MyState = IDLE_ST MyDestX, MyDestY = 0,0 TraceAI ("CHASE_ST -> IDLE_ST : ENEMY_OUTSIGHT_IN") return end local x, y = GetV (V_POSITION,MyEnemy) if (MyDestX ~= x or MyDestY ~= y) then -- DESTCHANGED_IN MyDestX, MyDestY = GetV (V_POSITION,MyEnemy) Move (MyID,MyDestX,MyDestY) TraceAI ("CHASE_ST -> CHASE_ST : DESTCHANGED_IN") return end end function OnATTACK_ST () TraceAI ("OnATTACK_ST") if (true == IsOutOfSight(MyID,MyEnemy)) then -- ENEMY_OUTSIGHT_IN MyState = IDLE_ST TraceAI ("ATTACK_ST -> IDLE_ST") return end if (MOTION_DEAD == GetV(V_MOTION,MyEnemy)) then -- ENEMY_DEAD_IN MyState = IDLE_ST TraceAI ("ATTACK_ST -> IDLE_ST") return end if (false == IsInAttackSight(MyID,MyEnemy)) then -- ENEMY_OUTATTACKSIGHT_IN MyState = CHASE_ST MyDestX, MyDestY = GetV (V_POSITION,MyEnemy) Move (MyID,MyDestX,MyDestY) TraceAI ("ATTACK_ST -> CHASE_ST : ENEMY_OUTATTACKSIGHT_IN") return end if (MySkill == 0) then local type = GetV (V_HOMUNTYPE,MyID) -- ホムの種族取得 ActionFlag = 0 -- スキルを含めて行動済みか否か -- ホムンクルスの種族ごとにスキル設定 if (type == FILIR or type == FILIR_H or type == FILIR2 or type == FILIR_H2) then MySkill = 8009 -- ムーンライトスキル(8009)は MySkillLevel = 5 -- 常にLv5で発動 elseif(type == VANILMIRTH or type == VANILMIRTH_H or type == VANILMIRTH2 or type == VANILMIRTH_H2) then MySkill = 8013 -- カプリススキル(8013)は MySkillLevel = 5 -- 常にLv5で発動 elseif(type == LIF or type == LIF_H or type == LIF2 or type == LIF_H2) then MySkill = 8001 -- リーフヒール(8001)は MySkillLevel = 5 -- 常にLv5で発動 else MySkill = 0 -- それ以外のホムでは発動しない MySkillLevel = 0 end -- 主人の敵を取得 local OwnerEnemy = GetV(V_TARGET,GetV(V_OWNER,MyID)) -- 主人の敵がこちらを攻撃しているか?  if ( HomunculusLoyalty==1 and OwnerEnemy>0 and OwnerEnemy~=MyID and (GetV(V_TARGET,OwnerEnemy)==GetV(V_OWNER,MyID) or GetV(V_TARGET,OwnerEnemy)==MyEnemy)) then MyEnemy = OwnerEnemy -- 主人の標的とホムの標的が異なれば、主人の標的を優先 end -- トリュフ掘り(対キノコ・草) if (GetV(V_HOMUNTYPE,OwnerEnemy) >= 1078 and GetV(V_HOMUNTYPE,OwnerEnemy) <= 1085) then MyEnemy = OwnerEnemy -- 主人がキノコ攻撃してれば手伝い優先 Attack (MyID,MyEnemy) TraceAI ("ATTACK_ST -> ATTACK_ST : ENERGY_RECHARGED_IN") MySkill = 0 MySkillLevel = 0 ActionFlag = 1 -- 行動済みフラグを立てる return end if (MySkill ~= 0 and MySkillLevel ~= 0) then -- スキルがセットされていないと発動しない -- ホムのHP/SP状態を取得 local HomunculusHP = GetV (V_HP,MyID) local HomunculusMaxHP = GetV (V_MAXHP,MyID) local HomunculusSP = GetV (V_SP,MyID) local HomunculusMaxSP = GetV (V_MAXSP,MyID) -- 主人のHP状態を取得 local OwnerHP = GetV (V_HP,GetV(V_OWNER,MyID)) local OwnerMaxHP = GetV (V_MAXHP,GetV(V_OWNER,MyID)) -- SPカツカツの場合は発動させない if (AutoSkillSPLimit < (HomunculusSP / HomunculusMaxSP)*100 and HomunculusSP ~= 0) then -- フィーリルとバニルミルト if (MySkill == 8009 or MySkill == 8013) then -- 残りSPによって発動確率を調整 local AutoSkillProbability = AutoSkill + ((HomunculusSP / HomunculusMaxSP) * 10) -- 賽を投げる local Dice = math.floor(math.random(100)) if (AutoSkillProbability > Dice and LastSkillEnemy ~= MyEnemy) then SkillObject (MyID,MySkillLevel,MySkill,MyEnemy) -- スキル攻撃 ActionFlag = 1 -- 行動済みフラグを立てる if (AutoSkillOnce == 1) then -- オートスキル一度だけなら LastSkillEnemy = MyEnemy -- 最後にスキルを使用した敵を把持 end TraceAI ("ATTACK_ST -> ATTACK_ST : AUTO_SKILL") end -- リーフ elseif (MySkill == 8001 and HomunculusHeal == 1) then if (HomunculusDeadlineHP > (HomunculusHP / HomunculusMaxHP)*100 or OwnerDeadlineHP > (OwnerHP / OwnerMaxHP)*100) then -- ホムの最新SPと前回SPを比較/ディレイ発生中はスキル連続使用しない if (HomunculusSP == HomunculusSPLast and HomunculusHealDelay > GetTick() ) then Attack (MyID,MyEnemy) -- 通常攻撃 TraceAI ("ATTACK_ST -> ATTACK_ST : AUTO_HEAL...FAILURE -> ENERGY_RECHARGED_IN") else SkillObject (MyID,MySkillLevel,MySkill,MyID) TraceAI ("ATTACK_ST -> ATTACK_ST : AUTO_HEAL") HomunculusHealDelay = GetTick() + 1000 end ActionFlag = 1 end end end end -- この時点で何も行動をしていなければ通常攻撃 if (ActionFlag == 0) then Attack (MyID,MyEnemy) TraceAI ("ATTACK_ST -> ATTACK_ST : ENERGY_RECHARGED_IN") ActionFlag = 1 -- 行動済みフラグを立てる end -- 後処理 MySkill = 0 MySkillLevel = 0 else SkillObject (MyID,MySkillLevel,MySkill,MyEnemy) MySkill = 0 TraceAI ("ATTACK_ST -> ATTACK_ST : USE_SKILL") end -- この時点で現在のホムSPを格納しておく(対リーフヒール用) HomunculusSPLast = GetV (V_SP,MyID) return end function OnMOVE_CMD_ST () TraceAI ("OnMOVE_CMD_ST") local x, y = GetV (V_POSITION,MyID) if (x == MyDestX and y == MyDestY) then -- DESTINATION_ARRIVED_IN MyState = IDLE_ST end end function OnSTOP_CMD_ST () end function OnATTACK_OBJECT_CMD_ST () end function OnATTACK_AREA_CMD_ST () TraceAI ("OnATTACK_AREA_CMD_ST") local object = GetOwnerEnemy (MyID) if (object == 0) then object = GetMyEnemy (MyID) end if (object ~= 0) then -- MYOWNER_ATTACKED_IN or ATTACKED_IN MyState = CHASE_ST MyEnemy = object return end local x , y = GetV (V_POSITION,MyID) if (x == MyDestX and y == MyDestY) then -- DESTARRIVED_IN MyState = IDLE_ST end end function OnPATROL_CMD_ST () TraceAI ("OnPATROL_CMD_ST") local object = GetOwnerEnemy (MyID) if (object == 0) then object = GetMyEnemy (MyID) end if (object ~= 0) then -- MYOWNER_ATTACKED_IN or ATTACKED_IN MyState = CHASE_ST MyEnemy = object TraceAI ("PATROL_CMD_ST -> CHASE_ST : ATTACKED_IN") return end local x , y = GetV (V_POSITION,MyID) if (x == MyDestX and y == MyDestY) then -- DESTARRIVED_IN MyDestX = MyPatrolX MyDestY = MyPatrolY MyPatrolX = x MyPatrolY = y Move (MyID,MyDestX,MyDestY) end end function OnHOLD_CMD_ST () TraceAI ("OnHOLD_CMD_ST") if (MyEnemy ~= 0) then local d = GetDistance(MyEnemy,MyID) if (d ~= -1 and d <= GetV(V_ATTACKRANGE,MyID)) then Attack (MyID,MyEnemy) else MyEnemy = 0 end return end local object = GetOwnerEnemy (MyID) if (object == 0) then object = GetMyEnemy (MyID) if (object == 0) then return end end MyEnemy = object end function OnSKILL_OBJECT_CMD_ST () end function OnSKILL_AREA_CMD_ST () TraceAI ("OnSKILL_AREA_CMD_ST") local x , y = GetV (V_POSITION,MyID) if (GetDistance(x,y,MyDestX,MyDestY) <= GetV(V_SKILLATTACKRANGE,MyID,MySkill)) then -- DESTARRIVED_IN SkillGround (MyID,MySkillLevel,MySkill,MyDestX,MyDestY) MyState = IDLE_ST MySkill = 0 end end function OnFOLLOW_CMD_ST () TraceAI ("OnFOLLOW_CMD_ST") local ownerX, ownerY, myX, myY ownerX, ownerY = GetV (V_POSITION,GetV(V_OWNER,MyID)) -- 持ち主 myX, myY = GetV (V_POSITION,MyID) -- 自分 local d = GetDistance (ownerX,ownerY,myX,myY) if ( d <= PatrolRange) then -- PatrolRange以下の距離なら return end local motion = GetV (V_MOTION,MyID) if (motion == MOTION_MOVE) then -- 移動中 d = GetDistance (ownerX, ownerY, MyDestX, MyDestY) if ( d > PatrolRange) then -- PatrolRange以上の距離なら MoveToOwner (MyID) MyDestX = ownerX MyDestY = ownerY return end else -- 他の動作 MoveToOwner (MyID) MyDestX = ownerX MyDestY = ownerY return end end function GetOwnerEnemy (myid) local result = 0 local owner = GetV (V_OWNER,myid) local actors = GetActors () local enemys = {} local index = 1 local target for i,v in ipairs(actors) do if (v ~= owner and v ~= myid) then target = GetV (V_TARGET,v) if (target == owner and GetV(V_HOMUNTYPE,v)~=1590) then -- プラントのジオは無視 if (IsMonster(v) == 1 and MOTION_DEAD ~= GetV(V_MOTION,v)) then enemys[index] = v index = index+1 else local motion = GetV(V_MOTION,i) if (motion == MOTION_ATTACK or motion == MOTION_ATTACK2) then enemys[index] = v index = index+1 end end end end end local min_dis = 100 local dis for i,v in ipairs(enemys) do dis = GetDistance2 (myid,v) if (dis < min_dis) then result = v min_dis = dis end end -- ホムンクルスの残りHPが一定%以下の場合は攻撃させない(=ターゲット放棄≒敵を取得しない) local HomunculusHP = GetV (V_HP,myid) local HomunculusMaxHP = GetV (V_MAXHP,myid) if ((HomunculusHP/HomunculusMaxHP)*100 < HomunculusSafetyHP) then result = 0 end return result end function GetMyEnemy (myid) local result = 0 result = GetMyEnemyA (myid) -- 種族に関係なくノンアクティブ化 -- どうしても常時アクティブにしたい場合は GetMyEnemyB (myid) -- ただし人の多い狩り場ではノーマーナーになるので注意!! return result end ------------------------------------------- -- 非先攻型 GetMyEnemy ------------------------------------------- function GetMyEnemyA (myid) local result = 0 local owner = GetV (V_OWNER,myid) local actors = GetActors () local enemys = {} local index = 1 local target for i,v in ipairs(actors) do if (v ~= owner and v ~= myid) then target = GetV (V_TARGET,v) if (target == myid) then enemys[index] = v index = index+1 end end end local min_dis = 100 local dis for i,v in ipairs(enemys) do dis = GetDistance2 (myid,v) if (dis < min_dis) then result = v min_dis = dis end end return result end ------------------------------------------- -- 先攻型 GetMyEnemy ------------------------------------------- function GetMyEnemyB (myid) local result = 0 local owner = GetV (V_OWNER,myid) local actors = GetActors () local enemys = {} local index = 1 local type for i,v in ipairs(actors) do if (v ~= owner and v ~= myid) then if (1 == IsMonster(v)) then enemys[index] = v index = index+1 end end end local min_dis = 100 local dis for i,v in ipairs(enemys) do dis = GetDistance2 (myid,v) if (dis < min_dis) then result = v min_dis = dis end end return result end function AI(myid) MyID = myid -- 最速攻撃対策(攻撃命令キューを再送信) if (ActionFlag == 1) then Attack (MyID,MyEnemy) ActionFlag = 0 end local msg = GetMsg (myid) -- command local rmsg = GetResMsg (myid) -- reserved command if msg[1] == NONE_CMD then if rmsg[1] ~= NONE_CMD then if List.size(ResCmdList) < 10 then List.pushright (ResCmdList,rmsg) -- 予約コマンド保存 end end else List.clear (ResCmdList) -- 新しいコマンドが入力されたら、予約コマンドは削除する ProcessCommand (msg) -- コマンド処理 end -- HPが残り少ない場合は緊急離脱モード -- ただし献身度が-1(自己犠牲)になっている場合は緊急離脱行動をとらずに戦闘継続させる local HomunculusHP = GetV (V_HP,MyID) local HomunculusMaxHP = GetV (V_MAXHP,MyID) if ((HomunculusHP/HomunculusMaxHP)*100 < HomunculusSafetyHP and HomunculusEscape == 1 and HomunculusDevotion ~= -1) then MyState = ESCAPE_ST if (EscapeSteps == 0) then EscapeSteps = 1 end end -- 状態処理 if (MyState == IDLE_ST) then OnIDLE_ST () elseif (MyState == ESCAPE_ST) then EMERGENCY_ESCAPE () elseif (MyState == CHASE_ST) then OnCHASE_ST () elseif (MyState == ATTACK_ST) then OnATTACK_ST () elseif (MyState == FOLLOW_ST) then OnFOLLOW_ST () elseif (MyState == MOVE_CMD_ST) then OnMOVE_CMD_ST () elseif (MyState == STOP_CMD_ST) then OnSTOP_CMD_ST () elseif (MyState == ATTACK_OBJECT_CMD_ST) then OnATTACK_OBJECT_CMD_ST () elseif (MyState == ATTACK_AREA_CMD_ST) then OnATTACK_AREA_CMD_ST () elseif (MyState == PATROL_CMD_ST) then OnPATROL_CMD_ST () elseif (MyState == HOLD_CMD_ST) then OnHOLD_CMD_ST () elseif (MyState == SKILL_OBJECT_CMD_ST) then OnSKILL_OBJECT_CMD_ST () elseif (MyState == SKILL_AREA_CMD_ST) then OnSKILL_AREA_CMD_ST () elseif (MyState == FOLLOW_CMD_ST) then OnFOLLOW_CMD_ST () end -- 最速攻撃対策(攻撃命令キューを再送信) if (ActionFlag == 1) then Attack (MyID,MyEnemy) end end