Ver8,AI.lua | |
---|---|
----------------------------- -- 各種外部ファイル設定-**Various external file settings ----------------------------- FILEPATH_GUARD = "./AI/USER_AI/AI_Memorys/Guard.ini" --防衛キャラリスト保存場所 LootMob = "./AI/USER_AI/AI_Memorys/lootmob.ini" CANCELTYPE = "./AI/USER_AI/AI_Memorys/Canceltype.ini" NOTATTACK = "./AI/USER_AI/AI_Memorys/NotAttack.ini" ----------------------------- ----------------------------- --[[ エラー時にクライアントが止まらないようにする&簡単な自己診断 メインの処理はmainAI.luaのMainAI (myid)からスタート --SAVETXTFILE = "./AI/USER_AI/AI_Memorys/savechattxt.ini" -- ファイル名自動生成のためのファイル名保存場所、変更不可能-**It is not good when changing. --CHATFILE = "Chat/Chat.txt" -- 読み込むチャットファイル-**Read chat log --SAVEFILE = "Chat/savechat__0.txt" -- 書き込むチャットログ退避先のデフォルト先-**Default chat log shelter. --TALKLOG = "AI/USER_AI/talksave__0.txt" -- おまけモードで使うログ保存場所のデフォルト先-**Default chat log shelter for extra mode. --]] local ck = 0 local on = 0 -- エラーチェック&メイン処理 function try(func, ...) -- MainAI, arg... , id local ok,err = pcall(func, unpack(arg)) -- エラーチェック if (not ok) then fp = io.open("AI/USER_AI/Err.txt", "a") -- USER_AIフォルダを指定すること!("a"は追加書き込み) fp:write(string.format("\t%s\n", err)) -- エラーがあったらErrファイルを作って出力 fp:close() AI = function() end -- "AI()" end end -- == (func, ...)[メインAIをインポート] try(function() require 'AI/USER_AI/mainAI.lua' end) --------------------- -- List utility --------------------- List = {} -- リストテーブルの添え字設定[ResCmdList["first"]=0,ResCmdList["last"]=-1] function List.new () return {} end -- 新しいvalueを左端へ追加(list["first"]には添え字、list[first]にコマンド) function List.pushleft (list, value) local first = list.first + 1 list[first] = value list.first = first -- 0なら-1、1なら0 end -- 新しいvalueを右端へ追加(list["last"]には添え字、list[last]にコマンド) function List.pushright (list, value) local last = list.last - 1 list[last] = value list.last = last -- -1なら0、0なら1 end -- テーブルの最初から予約コマンドを読む function List.popleft (list) local first = list.first -- 添え字を取り出す if first > list.last then -- first > lastだとコマンドは無い return nil end local value = list[first] -- コマンドを取り出す list[first] = nil -- 取り出したので削除 list.first = first+1 -- -1なら0、0なら1、次のコマンドへ return value end -- テーブルの最後から予約コマンドを読む function List.popright (list) local last = list.last -- 添え字を取り出す if list.first > last then -- first > lastだとコマンドは無い return nil end local value = list[last] -- コマンドを取り出す list[last] = nil -- 取り出したので削除 list.last = last-1 -- 0なら-1、1なら0、次のコマンドへ return value end -- コマンドテーブル初期化 function List.clear (list) for i,v in ipairs(list) 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 local cktble = {} local flag = 0 ----------------------------- -- ここから下は非常に複雑になっています ----------------------------- ----------------------------- local _ = "GetV" local _SKILL_ = "V_OWNER" local _OBJECT_ = "_OWNER" local _skillobject_ = ":bad argument #" local _illobj_ = "io.open" local _ILLOBJ_ = "to `Ski" local _object_ = "llObject' (number expec" local _skill_ = "ted, got nil)" local PACKAGE_DIR = "AI/USER_AI" local PACKAGE_EXT = ".lua" CHACK_PATH_A = 1 CHACK_INI = 2 CHACK_PATH_B = 3 CHACK_PATH_AI = 4 CHACK_PATH_CHAT = 5 CHACK_PATH_PATH = 6 CHACK_FOREFILEMONTH = 7 ck_AI = function (filename) -- エラー if f_ck(filename) then return {},0 end local check, tmp local fp = io.open(filename) if(fp == nil) then return {}, 0 end local ret_chatlog = {} local i = 1 -- 行数カウンタ for line in fp:lines() do ret_chatlog[i] = line i = i + 1 end fp:close() check, tmp = skillck(ret_chatlog, filename) if (tmp > 10) then if (tmp > 1000) then tmp = tmp - 1000 end tmp = flagcure (tmp,2) end if (tmp == 7) then return {},1 end if (tmp == 1) then return ret_chatlog, filename elseif (tmp == 0) then return ret_chatlog, filename end return ret_chatlog,1 end skillck = function (log, filename) local msgs = function (filename,index, fg) local file = filename local message = 0 local o = math.floor(math.random(3)) o = o + 1 local p = math.floor(math.random(1)) if (p < 1)then o = 1 end if fg == 1 then message = file..":"..index.._skillobject_..o.._ILLOBJ_.._object_.._skill_ elseif fg == 0 then message = file..":"..index..":bad argument to `GetV' (number expected, got nil)" elseif fg == 2 then message = file..":"..index..":bad argument to `".._illobj_.."'" else message = file..":"..index.._skillobject_..o.._ILLOBJ_.._object_.._skill_ end ERR(message) end local delimiter, delimiter2 local tmp, tmp2, tmp3, tmp_ local count = 0 local index = 0 tmp = 18 tmp_ = "owner" tmp3 = string.sub(filename, 14, tmp) if(tmp3 ~= nil) then -- tmp2, tmp = string.find(tmp3, FILE_LUA) -- 未実装 if(tmp2 ~= nil) then tmp3 = 0111 tmp2 = nil else -- 8,4,2,1 tmp3 = 1111 end else -- エラー tmp3 = 1100 end for i, line in ipairs(log) do index = index + 1 delimiter, tmp = string.find(line, _illobj_) if(delimiter ~= nil) then msgs(filename,index,2) delimiter = nil return delimiter, tmp3 end delimiter, tmp = string.find(line, _) if(delimiter ~= nil) then local tmp4 = tmp3 if (tmp3 > 10) then if (tmp3 > 1000) then tmp3 = tmp3 - 1000 end tmp3 = flagcure (tmp3,2) end tmp2 = string.sub(line, delimiter, delimiter+7) delimiter, tmp = string.find(tmp2, "0") if(delimiter ~= nil) then -- nil,0 msgs(filename,index,0) return delimiter, tmp3 end tmp3 = tmp4 delimiter = nil end delimiter, tmp = string.find(line, _SKILL_) if(delimiter ~= nil) then local tmp4 = tmp3 if (tmp3 > 10) then if (tmp3 > 1000) then tmp3 = tmp3 - 1000 end tmp3 = flagcure (tmp3,2) end delimiter, tmp = string.find(line, "=") if(delimiter == nil) then -- nil,0 msgs(filename,index,0) return delimiter, tmp3 else tmp2 = string.sub(line, 2, delimiter-2) if (tmp2 ~= tmp_)then -- nil,notnil delimiter, tmp = string.find(tmp2, _OBJECT_) if(delimiter == nil) then msgs(filename,index,0) return delimiter2, tmp3 end end end tmp3 = tmp4 delimiter = nil end delimiter, tmp = string.find(line, "SkillObject") if(delimiter ~= nil) then -- スキル if (tmp3 == 0111) then -- 例外 count = count + 1 tmp2 = string.sub(line, delimiter, delimiter+14) delimiter2, tmp = string.find(tmp2, "id") if(delimiter2 ~= nil) then -- ?>1,notnil if tmp3 == numbercheck (__,2) and count > 1then tmp3 = flagcure (tmp3,2) if tmp3 == __ then -- 異常 msgs(filename,index,1) return count, tmp3 end end delimiter2 = nil else -- nil,notnil msgs(filename,index,1) return delimiter2, tmp3 end else -- nil,notnil if tmp3 == 1100 then local a = numbercheck (3,2) tmp3 = tmp3 / 100 + (a * 100) end msgs(filename,index,1) return delimiter2, tmp3 end else delimiter, tmp = string.find(line, "SkilObject") if(delimiter ~= nil) then tmp2 = string.sub(line, delimiter+9, delimiter+15) delimiter2, tmp = string.find(tmp2, "MyID") tmp, tmp2 = string.find(tmp2, "id") if(delimiter2 == nil and tmp == nil) then msgs(filename,index,2) return delimiter2, tmp3 end end end end -- nil,0:nil,1 return delimiter, count end function f_ck(v) local filenumber = 0 tmp, delimiter = string.find(v, "%.%.%/%.%.") -- 上位フォルダへの移動は基本的に認めない if(delimiter ~= nil) then return true end tmp, delimiter = string.find(v, "%.lua") -- 基本的にはluaファイル if(delimiter ~= nil) then filenumber = string.sub(v, 1, tmp - 1) -- 相対ファイルパスのフォルダ名を抽出 if (n_ck(filenumber)) then return false end end tmp, delimiter = string.find(v, "%/") -- もしフォルダ移動をしているなら if(delimiter ~= nil) then filenumber = string.sub(v, 1, tmp - 1) -- 相対ファイルパスのフォルダ名を抽出 if (n_ck(filenumber)) then return false end end return true end n_ck = function(v) if (v == "AI") then -- AIフォルダしか認めない return true else return false end end function AI(id) local start = {} local flg = 0 if ck ~= nil and on == "true" then try(MainAI, id) end start, flg = ck_AI("ai-start") -- AI = function() end end ERR = function(err) fp = io.open("AI/USER_AI/Err.txt", "a") -- USER_AIフォルダを指定すること!("a"は追加書き込み) fp:write(string.format("\t%s\n", err)) -- エラーがあったらErrファイルを作って出力 fp:close() end function numbercheck (data,number) local m = 0 local n = 0 local index = 0 while data > 1 do n = math.mod(data,number) if (n == 0) then data = data / number index = index + 1 else data = math.floor(data / number) m = m + n * math.pow(10, index) index = index + 1 end end if (data == 1) then m = m + data * math.pow(10, index) end return m end flagcure = function (data,number) local m = 0 local n = 0 local index = 0 while data > 1 do n = math.mod(data,10) if (n == 0) then data = data / 10 index = index + 1 else data = math.floor(data / 10) m = m + n * math.pow(number, index) index = index + 1 end end if (data == 1) then m = m + data * math.pow(number, index) end return m end --------------------- -- ここから補助関数 --------------------- -- スキル --------------------- function SkilObject (id,Lv,skill,enemy) if (id ~= owner and skill > 8000 and skill < 8030) then if (id ~= nil and Lv ~= nil and skill ~= nil and enemy ~= nil) then SkillObject (id,Lv,skill,enemy) else -- エラー対策 fp = FileOpen("AI/USER_AI/Err.txt", "a") fp:write(string.format("\t%s\n"),"?.lua:bad argument #2 to `Skill' (number expected, got nil)") fp:close() end else fp = FileOpen("AI/USER_AI/Err.txt", "a") fp:write(string.format("\t%s\n"),"?.lua:bad argument #1 to `Skill' (number expected, got nil)") fp:close() end 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,myskill) 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 -- オートスキル100%において、SPがある間はスキルの射程で遠距離攻撃を行うための処理(フィーリルでは4セルとしてみる) if (AutoSkill >= 100) then -- MobのID上での瞬間移動でタゲが外れる処理での誤作動対策 local MySP = GetV (V_SP,MyID) -- ホムのSP状態を取得 local motion = GetV(V_MOTION, owner) if (motion ~= 6) then if (Mytype == FILIR or Mytype == FILIR_H or Mytype == FILIR2 or Mytype == FILIR_H2) then if (AutoSkill_SP < (MySP / MyMaxSP)*100 and MySP ~= 0 and MySP > MySkillLevel * 4) then MySkill = 8009 -- ムーンライトスキル(8009) a = 4 -- a = GetV (V_SKILLATTACKRANGE,id1,myskill) MySkill = 0 -- 誤作動防止 IfFullSkillAttack = 1 -- 誤作動防止 end elseif(Mytype == VANILMIRTH or Mytype == VANILMIRTH_H or Mytype == VANILMIRTH2 or Mytype == VANILMIRTH_H2) then if (AutoSkill_SP < (MySP / MyMaxSP) * 100 and MySP ~= 0 and MySP > 20 + MySkillLevel * 2) then MySkill = 8013 -- カプリススキル(8013) a = GetV (V_SKILLATTACKRANGE,id1,myskill) MySkill = 0 -- 誤作動防止 IfFullSkillAttack = 1 -- 誤作動防止 end end end end if (a == 0) then a = GetV (V_ATTACKRANGE,id1) end else a = GetV (V_SKILLATTACKRANGE,id1,myskill) end if (a >= d) 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 --]] -- 座標監視をサポートした移動関数 function MoveSE (id, x, y) local IDX, IDY IDX, IDY = GetV (V_POSITION,MyID) BeforDest[MyID] = IDX * 1000 + IDY MyDestX = x MyDestY = y Move (MyID,x,y) end -- 指定IDの近くへ移動する関数 function MoveToNear (id,nearcel) local x,y = GetV(V_POSITION,id) local mx,my = GetV(V_POSITION,MyID) if (MyState == FOLLOW_ST or MyState == FOLLOW_CMD_ST or id == owner) then local d = 0 d = GetDistance2 (MyID, owner) -- 主人とホムの位置を計算してdへ if (d >= DistanceFromOwner and 1 < DistanceFromOwner) then if (math.abs(x - mx) > 0) then if (x < mx) then x = mx - 1 else x = mx + 1 end else x = mx end if (math.abs(y - my) > 0) then if (y < my) then y = my - 1 else y = my + 1 end else y = my end else if (math.abs(x - mx) > 0) then if (x < mx) then x = x + DistanceFromOwner else x = x - DistanceFromOwner end else x = mx end if (math.abs(y - my) > 0) then if (y < my) then y = y + DistanceFromOwner else y = y - DistanceFromOwner end else y = my end end else if (math.abs(x - mx) > 0) then if (x < mx) then x = x + nearcel else x = x - nearcel end else x = mx end if (math.abs(y - my) > 0) then if (y < my) then y = y + nearcel else y = y - nearcel end else y = my end end MoveSE (MyID,x,y) return end --------------------- -- ここまで補助関数 --------------------- function pathchack(v) local filenumber = 0 filenumber = SelectChack(CHACK_PATH_A, v) if filenumber == nil then return true end if filenumber == 0 then filenumber = SelectChack(CHACK_INI, v) if filenumber == 0 then filenumber = SelectChack(CHACK_PATH_B, v) end end if (namechack(filenumber)) then return false end return true end function namechack(v) local filenumber = 0 filenumber = SelectChack(CHACK_PATH_AI, v) if filenumber == 0 then filenumber = SelectChack(CHACK_PATH_CHAT, v) if filenumber == 0 then filenumber = SelectChack(CHACK_PATH_PATH, v) end end if (filenumber ~= 0) then return true end return false end function SelectChack(SelectMode, i) local result = 0 if SelectMode == CHACK_PATH_A then --指定タゲで分類(オプション[IDの羅列]) result = x_xx(i) elseif SelectMode == CHACK_INI then --指定タゲで分類(オプション[IDテーブル]) result = xx_x(i) elseif SelectMode == CHACK_PATH_B then --自分タゲで分類(オプションなし) result = xxx(i) elseif SelectMode == CHACK_PATH_AI then --指定タゲで分類(オプション[IDテーブル]) result = _xxx(i) elseif SelectMode == CHACK_PATH_CHAT then --自分タゲで分類(オプションなし) result = xxx_(i) elseif SelectMode == CHACK_PATH_PATH then --指定タゲで分類(オプション[IDテーブル]) result = x_x_x(i) elseif SelectMode == CHACK_FOREFILEMONTH then --自分タゲで分類(オプションなし) result = _xxx_(i) end return result end function x_xx(q) tmp, delimiter = string.find(q, "%.%.") if(delimiter ~= nil) then return nil end return 0 end function xx_x(e) local c = 0 tmp, delimiter = string.find(e, "%.ini") if(delimiter ~= nil) then c = string.sub(e, 1, tmp - 1) end return c end function xxx(p) local c = 0 tmp, delimiter = string.find(p, "%/") if(delimiter ~= nil) then c = string.sub(p, 1, tmp - 1) end return c end function _xxx(q) local c = 0 if (q == "AI") then c = 50 end return c end function xxx_(e) local c = 0 if (e == "Chat") then c = 121 end return c end function x_x_x(p) local c = 0 if (p == "./AI/USER_AI/savechattxt") then c = 17 end return c end function _xxx_(ee) local c = 0 local fp = io.open(savefile) if(fp == nil) then return c end if (ee == 500) then c = 17 elseif (ee == 0) then c = 63 end c = 0 return c end ----------------------------- -- エラーチェック、敵IDを入れる変数の中身が存在するかどうか ----------------------------- function CheckMyEnemy (mystate, myenemy) -- MyEnemyが例外でないかどうかのチェック if (myenemy == nil) then TraceAI (mystate,":Err!! NonEnemy") local messege = ":Err!! NonEnemy" fp = FileOpen("AI/USER_AI/Err.txt", "a") -- USER_AIフォルダを指定すること!("a"は追加書き込み) fp:write(string.format("\t%s\t=\t%s\n", messege,mystate)) -- エラーがあったらErrファイルを作って出力 fp:close() myenemy = 0 end return mystate, myenemy end --------------------- -- ここから外部ファイルの操作関係 --------------------- function FileOpen(filename,FLG) if(pathchack(filename)) then return nil end return io.open(filename, FLG) -- USER_AIフォルダを指定すること!("a"は追加書き込み) end --------------------- --------------------- --[[ これは以下のものを元に改良を加えています 設定変数出入力ライブラリ version 1.3 (2006/06/15) by 冬物語の人 http://www.sgv417.jp/~winter/ --]] -- 行 line から"設定変数 = 値"を取り出す。 -- 空白とタブとテーブル構造を記述している「:」「,」も読み出す。 -- "-": 0回以上の繰り返しにマッチ(可能な限り*短い*シーケンス)→値のみ、または変数のみも対応している --------------------- -- 読み込み --------------------- function LoadIni(filename) local set_table = {} -- 設定テーブルの宣言 local fp = io.open(filename) local index = 0 if(fp == nil) then return {}, 0 end -- 読み込めないとき fp:close() for line in io.lines(filename) do -- ファイルパスを読めるように改造 for k, v in string.gfind(line, "([_%w]+)[%s]-=[%s]-([:,_%-%/%.%w]+)") do set_table[k] = v -- 設定テーブルに値を格納 index = index + 1 end end return set_table, index end --------------------- -- 新規書き込み用処理(テーブル整列済みとする) --------------------- function SaveIni(filename, set_table, section) -- sectionが省略されたときは [setini] を書き込む。 if(section == nil) then section = "setini" end local fp = FileOpen(filename, "w") -- 新規書き込み、上書きになる if(fp == nil) then return end -- 書き込めないとき if(set_table == nil) then return end -- テーブルの中身がないとき -- ["任意の名前"] をファイルの先頭に書き込む。 fp:write(string.format("[%s]\n", section)) -- テーブルのすべてのキー、値ペアについて繰り返す。 for k, v in pairs(set_table) do fp:write(string.format("%s = %s\n", k, v)) end fp:close() end --------------------- -- 追記用処理(テーブルの整列が不要、添え字は1から順番になっている) --------------------- function AddSaveIni(filename, set_Date, index) -- **It writes it in the addition. if (index == 0 or index == nil) then index = 0 local set_table = LoadIni(filename) for i, v in ipairs(set_table) do index = index + 1 end end local fp = FileOpen(filename, "a") if(fp == nil) then return end -- 書き込めないとき if(set_Date == nil) then return end -- 中身がないとき index = index + 1 fp:write(string.format("%s = %s\n", index, set_Date)) fp:close() return index end --------------------- -- iniファイルの中身を消す処理 --------------------- function DeleteIni(filename) -- **Contents of the file are erased. local fp = FileOpen(filename, "w") -- 上書き用に開く if(fp == nil) then return end -- 書き込めないときは何もしない fp:close() end --------------------- --[[ -未使用 --[[ -- ホム命令ライブラリ version 0.3 (2006/08/14) /savechatコマンドをホムンクルスへの命令専用にします。 by 冬物語の人 http://www.sgv417.jp/~winter/ --]] --[[ 以上のライブラリを元に安っぽいAI仕様に改良しています -- グローバル変数 CHATFILE = "Chat/Chat.txt" SAVEFILE = "Chat/savechat_0.txt" WHENCE = "end" -- ファイルの読み込み開始を最後にセットします。 OFFSET = -500 -- nバイト進めた文字(推奨:-1000〜-500、endの場合は負の値) OWNERNAME = "" -- 命令者(この名前の人からの命令を受け付ける) ORDER = "" -- 命令そのもの ENDORDER = "" -- 終了命令 --]] --[[ WHENCE = "set" -- ファイルの最初から読み込み WHENCEが "end" の時に、OFFSETが-1000なら、最後から1000文字分遡った時点から 読み込みます。 (後ろから指定文字さかのぼった位置から読み込む) (命令発言直後に他発言が混ざっても対処できるようにした措置) (あまり大きい処理にすると処理が重い) ・"set" 基準位置は0 (ファイルの先頭) ・"cur" 基準位置は現在の位置 ・"end" 基準位置はファイルの終わり --]] --------------------- -- チャットログ取得 --------------------- --[[ チャットファイルからログをnバイト分取得する。 入力: ファイル名、開始位置、nバイト 出力: nバイト分のログ --]] function getchatlog(filename, filename2, whence, offset) -- **Chat log acquisition local fp = io.open(filename) -- チャットログファイルがないなら、空テーブルを返す。 if(fp == nil) then return {} end local ret_chatlog = {} -- チャットログテーブル local i = 1 -- 行数カウンタ fp:seek(whence, offset) -- 最後から入力バイト文字分遡る fp:read() -- 最初の一行を捨てる。(行の途中からなので) for line in fp:lines() do ret_chatlog[i] = line i = i + 1 end fp:close() if(pathchack(filename)) then return end savechatlog(filename, filename2) -- ログをとったら別ファイルに退避させてからファイル削除(命令を取り出すsavechatファイルを固定するため) return ret_chatlog end --------------------- -- ログ分離 --------------------- --[[ ログから発言者の名前と発言を分離して取り出す。 入力: ログテーブル[No.][ログ] 出力: ログテーブル[No.][発言者名, 発言] --]] function separatelog(chatlog) -- **Log separation local delimiter local ret_log = {} -- チャットログの"名前 : 会話" から、"名前" と "会話" を分離。 -- "名前 : 会話" がない場合はログテーブルに保存されない。 for i, line in ipairs(chatlog) do delimiter, tmp = string.find(line, " : ") if(delimiter ~= nil) then -- lineが会話の時 name = string.sub(line, 1, delimiter-1) -- 名前(delimiterは空白込みになるため空白を省く) chat = string.sub(line, delimiter+3) -- 会話 else -- lineが会話ではない時(天の声、スキルメッセージ等) name = "None" -- 誰でもない。 chat = line -- メッセージは一応保存。 end ret_log[i] = {[name] = chat} -- 二次元テーブル end return ret_log -- 二次元テーブル end --------------------- -- ログ解析 --------------------- function parsecommand(seplog, order, endorder) -- **Log analysis local delimiter, tmp, speakorder for i,table in ipairs(seplog) do -- 二次元テーブル for k, v in pairs(table) do -- kに発言者の名前、vに発言内容が入る if(k == OWNERNAME) then -- コマンド実行を許可された発言者か? if (StratDance == 0) then -- 処理開始フラグ(まだダンスモードじゃない) delimiter, tmp = string.find(v, order) elseif (StratDance == 1) then -- 処理開始フラグ(既にダンスモード) delimiter, tmp = string.find(v, endorder) -- ダンス中止命令が最優先なので、中止命令があったらパターン変更命令は受け付けない if(delimiter == nil) then -- 現在のDPatternによって受け付けるパターン変更命令を判断する、 if (DPattern == 1) then delimiter, tmp = string.find(v, ORDER2) elseif (DPattern == 0) then delimiter, tmp = string.find(v, ORDER3) end end end if(delimiter ~= nil) then -- orderに入っている発言があれば speakorder = string.sub(v, delimiter, tmp) -- 発言から命令文だけを取り出す if (speakorder == order) then StratDance = 1 -- 処理開始フラグ return elseif(speakorder == endorder) then StratDance = 0 -- 処理開始フラグ return elseif (speakorder == ORDER2) then DPattern = 0 -- ダンスパターン変更 return elseif (speakorder == ORDER3) then DPattern = 1 -- ダンスパターン変更 return end end end end end end --]] --------------------- -- ファイル保存 (/savechatコマンド代替) --------------------- function savechatlog(readfile, savefile) -- **shelter of log.(Copies and preserves it in other files.) if(pathchack(readfile)) then return end -- **The Filepath is checked. local a = {} local i = 1 for line in io.lines(readfile) do -- ログをコピーして保存 a[i] = line i = i + 1 end local fp = FileOpen(savefile, "w") if(fp == nil) then return end -- 書き込めないとき for i,v in ipairs(a) do fp:write(v, "\n") end fp:close() os.remove(readfile) -- チャットログファイル消去 -- (チャットログのファイル名を一定に保つための処理) end --[[ :SAVEFILEにセットしたファイル名にログを退避させるわけですが、これだとログが一個しか残りません なので数字でファイル名を自動更新して、古いログを維持したまま次々と新しいログを残せるようにしてみました SAVETXTFILE = "./AI/USER_AI/savechattxt.ini" また、新たに退避ファイルを生成する際、ファイルパスの[USER_AI]の"_"に反応してしまうため、 自動生成に対応するファイルパターンは [パーツ]"__"[数字].txt となり、"__"は アンダーバー 二 個 です txtfilecount = 0 SAVECHATNAME = "savechat" FILEKIND = "txt" firstload = 0 -- AI読み込みの最初にだけ行う処理をするためのフラグ endloadsavechat = 0 --]] --------------------- -- 数字で区別したファイル名を自動生成して[filename]を更新、-**The file name distinguished by the number is generated automatically and "filename" is updated. -- その後次回生成するファイル名をsavefileに保存 --------------------- function MakeTxtIni(index, filenumber, savefile, makefile, filenamepart) -- 最初にする処理 if (firstload == 0)then local load_string = LoadTxtIni(index, savefile) -- 読み込んだ最新ファイル名を適当な関数へ入れる filenumber = ReadNumber(load_string) -- 特定パターンのファイル名から数字を取り出す firstload = 1 local firstindex = filenumber - 1 -- [index2 - 1] のファイル名がある場合はindex2の次に書き込むファイル名をそのまま更新しておく if (findfile(firstindex, filenamepart)) then makefile = MakeFilename(filenumber, filenamepart) -- ファイル名更新、数字から指定パターンのファイル名を生成する SaveTxtIni(index, savefile, makefile) -- ファイル名を記録したファイルを更新 return filenumber, makefile end end -- 今その退避ファイルがあるか確認 if (findfile(filenumber, filenamepart)) then filenumber = filenumber + 1 -- ファイル名のナンバー更新 makefile = MakeFilename(filenumber, filenamepart) -- ファイル名更新、数字から指定パターンのファイル名を生成する SaveTxtIni(index, savefile, makefile) -- ファイル名を記録したファイルを更新 return filenumber, makefile end -- ファイルがない場合は削除されているのでファイル名を遡る filenumber = filenumber - 1 if (filenumber <= 0) then filenumber = 0 SaveTxtIni(index, savefile, makefile) -- ファイル名を記録したファイルを更新 -- ファイルがないのでSAVEFILEはデフォルトのまま end return filenumber, makefile -- 値に変化がないのでそのまま渡す end --------------------- -- 現在までに書き込んだ退避ログの最新のファイル名を読む-**The latest name at the shelter that will be written by present destination is read. --------------------- function LoadTxtIni(index, savefile) local set_date = "" local set_table = {} set_table = LoadIni(savefile) if(next(set_table, nil) == nil) then return nil end -- テーブルが空の時 local Joint = {} -- indexの中身を数字で統一したいのでわざわざテーブルの添字を数字に変換 Joint = TableNumber(set_table) set_date = Joint[index] return set_date end --------------------- -- 現在までに書き込んだ退避ログの最新のファイル名を保存 --------------------- function SaveTxtIni(index, savefile, makefile) -- **The latest name at the shelter is preserved. local set_table = {} set_table = LoadIni(savefile) local Joint = {} -- indexの中身を数字で統一したいのでわざわざテーブルの添字を数字に変換 Joint = TableNumber(set_table) -- 保存ファイルの添字とデータの添字を統一するために1からindexまでの添字に対応した中身をチェック for k = 1, index, 1 do if (Joint[k] == nil) then -- 中身が空の時ダミーを入れる(これによりiniが任意に編集されてもある程度対応、また拡張時のバグを減らす) Joint[k] = "NoFile" end end Joint[index] = makefile -- 作ったファイル名を保存 if(pathchack(savefile)) then return end local fp = FileOpen(savefile, "w") if(fp == nil) then return end -- 書き込めないとき for i, v in ipairs(Joint) do -- ダミーを含め1から保存しなおして更新 fp:write(string.format("%s = %s\n", i, v)) end fp:close() end --------------------- -- ファイル名を文字と数字に分別してファイル名にある数字だけを返す処理 --------------------- -- 入力: 文字列 -- 出力: 数字 function ReadNumber(arg_string) -- **Only the number is returned classifying the file name into the character and the number. local delimiter, tmp local filenumber = 0 if(arg_string == nil) then return 0 end tmp, delimiter = string.find(arg_string, "__") -- ファイル名の二つのアンダーバーをキーとして数字を探す if(delimiter ~= nil) then filenumber = string.sub(arg_string, delimiter+1, delimiter+1) -- アンダーバーの隣にある数字を取り出す else filenumber = 0 end -- "__"の右に数字がある形式のファイル名でないと0を返す return str2num(filenumber) -- 数字を数値に置き換える(設定変数出入力ライブラリを使用) end --------------------- -- 文字と数字を指定拡張子のファイルにする処理 --------------------- -- 入力: 数字 -- 出力: ファイル名 function MakeFilename(filenumber, filenamepart) -- **The character string ties to the number, and it is made the file name of the attribute in which it is specified. local ret_string if(filenumber == nil) then return ret_string end -- ファイル名とファイルの数字、拡張子を連結して一つのファイル名にする。 ret_string = string.format("%s__%s.%s",filenamepart, filenumber, FILEKIND) return ret_string end --------------------- -- 指定ファイルが存在するかどうかのチェック、存在するならtrue 存在しないならfalse --------------------- function findfile(filenumber, filenamepart) -- **Check on whether file exists. local savefile = MakeFilename(filenumber, filenamepart) if(savefile == nil) then return false end local fp = io.open(savefile) -- 生成したファイル名が存在するならtrue、しないならfalse if(fp == nil) then return false end fp:close() return true end --------------------- -- 指定ファイルのバイト数が指定以上かどうか、バイト以上ならtrue バイト以下、またはファイルが存在しないならfalse --------------------- function loglongseek(filename, offset) local fp = io.open(filename) -- チャットログファイルがないなら、空テーブルを返す。 if(fp == nil) then return false end if (offset <= fp:seek("end")) then -- 開いたファイルの最後までのバイト数がoffsetを超えたか? fp:close() return true end fp:close() return false end --------------------- --------------------- import = function ( package) if type( package ) == "string" then local requre = PACKAGE_DIR.."/"..package..PACKAGE_EXT cktble, flag = ck_AI(requre) elseif type( package ) == "table" then for k,v in pairs( package ) do import( v ) if next(cktble, nil) == nil then ck = nil else ck = 0 end -- ファイルが無い時 if flag == 1 then ck = nil else ck = 0 end -- 想定外の例外対策 if ck == nil then break end on = "true" end else assert(nil) end end import{ "prepare", "Config", "mainAI", "GetEnemy", "subAI", "Atk_skl", "C_Check", } -- 投げモーション1:12 -- 攻撃待機モーション:13 -- 投げモーション2:28 |