--[[ RacerAI version 0.6 (2007/07/16) Lif Only. リーフ専用。 by 冬物語の人 http://winter.sgv417.jp/ Special Thanks: ちずきち様 青いリボンで結びます http://blue.ap.teacup.com/ribbon/ --]] local AI_DIR = "AI/" -- メインAI --local AI_DIR = "USER_AI/" -- ユーザーAI -- ターゲットID: 左が最も優先度高く、右へ行くにつれて優先度が下がります。 -- モンスターID: エンペリウム、黒いきのこ、赤いきのこ local TYPEIDS = {1288, 1084, 1085} local MINDIS = 5 -- 最低攻撃範囲(これ以上離れていれば安息) local SKIP = 5 -- AI()内の処理周期をSKIP回省略 local SKIPCOUNT = 0 -- 処理飛ばし用カウンタ local USE_MC = 1 -- モーションキャンセル使用(0:未使用 / 1:使用) local MOTION = 0 -- モーションキャンセル初期設定 -- 定数 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 (ホムンクルスと主人) -- 絶対値を求める function abs(number) if(number < 0) then return -number else return number end end -- (x1, y1)と(x2, y2)の最短距離を求める -- ※円ではなく、方形 -- 円: return math.floor(math.sqrt((x1-x2)^2+(y1-y2)^2)) function GetSquareDistance(x1, y1, x2, y2) local xpos = abs(x1 - x2) -- X座標上の距離 local ypos = abs(y1 - y2) -- Y座標上の距離 if(xpos >= ypos) then return xpos else return ypos end end -- id1とid2の最短距離を求める 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 -- X, Yのどちらかが負なら不正値 return -1 end return GetSquareDistance(x1, y1, x2, y2) end -- 現在残っているHPの% function GetHPPercent(id) return (GetV(V_HP, id) / GetV(V_MAXHP, id)) * 100.0 end -- 現在残っているSPの% function GetSPPercent(id) return (GetV(V_SP, id) / GetV(V_MAXSP, id)) * 100.0 end -- スキルLvセット: スキルレベル、範囲外の時にセットする数字 function SetSkillLv(skill, setlv) if IsNumber(skill) then -- スキルレベルが数字であれば if skill < 1 and setlv < skill then -- Lv範囲チェック skill = setlv end else -- スキルレベルが数字でない(引数なしのnilも含む) skill = setlv end return skill end -- 引数は数字型か? 真:true, 偽:false function IsNumber(arg) if type(arg) == "number" then return true else return false end end -- カプリス, SkillLv省略可 -- 消費SP:20+SkillLv×2, Lv1-3 function Caprice(myid, targetid, skill) local lv = SetSkillLv(skill, 5) -- 何もなければ最大Lv5をセット local sp = 20 + (lv * 5) if GetV(V_SP, myid) >= sp then SkillObject(myid, lv, 8013, targetid) -- カプリス end end -- メンタルチェンジ:ホムSP100以上, SkillLv省略可 -- 消費SP:固定, Lv1-3 function MentalChange(myid, skill) local lv = SetSkillLv(skill, 3) -- 何もなければ最大Lv3をセット if GetV(V_SP, myid) >= 100 then SkillObject(myid, lv, 8004, myid) -- メンタルチェンジ end end -- 緊急回避:ホムSP40以上, SkillLv省略可 -- 消費SP:15+SkillLv×5, Lv1-5 function UrgentEscape(myid, skill) local lv = SetSkillLv(skill, 5) -- 何もなければ最大Lv5をセット local sp = 15 + (lv * 5) local mysp = GetV(V_SP, myid) -- ホムの現在SP if((100 <= mysp) and (mysp <= 150)) then -- SPが100以上〜150以下ならメンタルチェンジでSPを全快 MentalChange(myid) end if mysp >= sp then SkillObject(myid, lv, 8002, myid) -- 緊急回避 end end -- 安息:ホムHP80%以上かつ主人SP50以上, SkillLv1固定 -- 消費SP:50 function Vaporize(myid, ownerid) if GetHPPercent(myid) >= 80 and GetV(V_SP, ownerid) >= 50 then SkillObject(myid, 1, 244, myid) -- 安息 end end -- TypeIDを探索して目標IDを見つけたら情報を返す function SeekTypeID(actors, TYPEIDS) local target = {} -- ターゲット情報テーブル target.exist = false -- ターゲット発見/未発見 -- ターゲットを探す for index, typeid in ipairs(TYPEIDS) do for i, v in ipairs(actors) do if(typeid == GetV(V_HOMUNTYPE, v)) then target.exist = true -- 発見 target.id = v -- ターゲットID target.x, target.y = GetV(V_POSITION, v) -- 位置 break end end -- ターゲットは先に見つけたものだけにする。 if target.exist then break end end return target end -- モーションキャンセル from 初回限定AI function MotionCancel(myid, myenemy) zx, zy = GetV(V_POSITION, myenemy) if(MOTION == 0) then Move(myid, zx+1, zy) Move(myid, zx, zy+1) MOTION = 1 else Move(myid, zx-1, zy) Move(myid, zx, zy-1) MOTION = 0 end end function AImain(myid) -- myid: ホムID local ownerid = GetV(V_OWNER, myid) -- ホムの主人ID local actors = GetActors() -- IDテーブル local msg = GetMsg(myid) -- 命令を受ける local rmsg = GetResMsg(myid) -- 予約命令を受ける local boost = 0 -- 攻撃速度加速 -- コマンド処理 if msg[1] == 1 then -- MOVE_CMD (Alt+右クリック) Move(myid, msg[2], msg[3]) elseif msg[1] == 3 then -- ATTACK_OBJECT_CMD (対象をAlt+右クリック) Attack(myid, msg[2]) elseif msg[1] == 7 then -- SKILL_OBJECT_CMD (ホムスキル) SkillObject(myid, msg[2], msg[3], msg[4]) -- Alt+Tを押している間のみ、攻撃速度を通常に戻す elseif msg[1] == 9 then -- FOLLOW_CMD (Alt+T) boost = 1 end -- 画面内から目標のTypeIDを探して情報を返す target = SeekTypeID(actors, TYPEIDS) -- ターゲットが死亡したらすぐ安息する if(GetV(V_MOTION, target.id) == 3) then Vaporize(myid, ownerid) end -- ターゲットが視野範囲内にいるかどうか? if(target.exist) then -- 実行を一定回数省略 SKIPCOUNT = SKIPCOUNT+1 if(SKIPCOUNT > SKIP or boost == 1) then -- SKIP回を超えたら SKIPCOUNT = 0 else -- まだSKIP回になっていない return end -- 目標までの距離を取得 distance = GetDistance2(myid, target.id) -- 目標に隣接していない if 2 < distance and distance <= MINDIS then MentalChange(myid) -- メンタルチェンジ Move(myid, target.x, target.y) -- ターゲットと同じ位置に移動 -- 時々ターゲットの隣にはじき出されます。 -- 目標距離が2以内になれば攻撃開始 elseif distance <= 2 then -- Caprice(myid, target.id) Attack(myid, target.id) -- ターゲットを攻撃 if(USE_MC == 1) then MotionCancel(myid, target.id) -- モーションキャンセル end -- 目標から遠く離れている else -- 自動緊急回避&自動安息 UrgentEscape(myid) Vaporize(myid, ownerid) end -- ターゲットが視野範囲内にいない else -- 自動緊急回避&自動安息 UrgentEscape(myid) Vaporize(myid, ownerid) end end -- エラーが出たら空のAI関数に差し替える function try(func, ...) local ok, err = pcall(func, unpack(arg)) if(not ok) then ErrorOut("Error.txt", err) -- エラー内容をファイルへ AI = function() end -- エラーが出たらAI()関数を空にする end end -- エラー内容をファイルに出力 function ErrorOut(str1 ,str2) local fp = io.open(string.format("./"..AI_DIR.."%s", str1), "a") if(fp == nil) then return end fp:write(string.format("%s : %s\n", os.date("%Y/%m/%d %H:%M:%S"), str2) ) fp:close() end -- エラー対策 function AI(myid) try(AImain, myid) end