戻る

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


戻る

戻る