|
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
|
|