Luaの変数をNScripterの文字列変数に保存する。

たとえば、ゲームのキャラクターのデータをnslua側でテーブルの形で持っているとします。

local chara = {}
chara["技術点"] = 10
chara["体力点"] = 20
chara["装備"] = {}
chara["装備"]["右手"] = "幅広の剣"
chara["装備"]["左手"] = "革の盾"

これをNScripterの文字列変数に折りたたんで格納すると、NScripter側でsaveすることで状態を保存できます。

ec.save_object(chara, 100) -- 文字列変数100番に格納する。

復帰させる時はこうします。

ec.load_object(chara, 100) -- 100番の中身を展開し、charaの中身をそれに書き換える。

保存できるのは、Luaのテーブル・文字列(255文字まで)・数値(整数のみ)・真偽値の4種類だけです。

スクリプト

下記のスクリプトを適当なファイル(たとえば、ec.lua)にコピペして保存して、system.luaの中から

NL_dofile("ec.lua")

等してください。

-- expand and convergence
module("ec", package.seeall)

function head_chop(t) -- テーブルの先頭を切り落として返す。
	return table.remove(t, 1)
end

function head_letter(t) -- テーブルの先頭を切り落として文字にして返す。
	return string.char(head_chop(t))
end

function load_object(t, num) -- 何かをNScripterから読み出した状態に変更する。
	if type(t) ~= "table" then return nil end -- テーブルしか受け付けません。
	local temp = object_expand(NSGetStrValue(num))
	if type(temp) ~= "table" then return nil end -- 読み出したものもテーブルでないとダメ。
	table_copy(temp, t)
	return t
end

function table_copy(from, to) -- テーブルの中身をコピーする。
	if type(from) ~= "table" then return nil end
	if type(to) ~= "table" then return nil end
	for k, v in pairs(to) do if type(from[k]) == "nil" then to[k] = nil end end
	for k, v in pairs(from) do to[k] = v end
	return to
end

function save_object(ob, num) -- 何かをNScripterの文字列変数に格納する。
	NSSetStrValue(num, object_convergence(ob))
end

function object_expand(str)
	return expand({str:byte(1, str:len())})
end

function object_convergence(ob) -- 何かを収束させる。
	return convergence(ob)
end

function expand(str_table) -- 何かを展開させる。
	local vt = head_letter(str_table)
	if vt == "n" then return expand_num(str_table) end
	if vt == "s" then return expand_str(str_table) end
	if vt == "b" then return expand_bool(str_table) end
	if vt == "t" then return expand_table(str_table) end
	return nil
end

function convergence(v) -- 何かを収束させる。
	local vt = type(v)
	if vt == "number" then return convergence_num(v) end
	if vt == "string" then return convergence_str(v) end
	if vt == "boolean"  then return convergence_bool(v) end
	if vt == "table" then return convergence_table(v) end
	return ""
end

function expand_num(str_table) -- 数値を展開する。
	local num = 0
	local sign = head_letter(str_table) -- 最初の数値を取得。
	if sign == "z" then return 0 end -- 0ならば0を返す。
	local digit = head_chop(str_table) -- 桁数を取得。
	for i = 1, digit do
		num = num * 256
		num = num + head_chop(str_table)
	end
	if sign == "m" then num = 0 - num end
	return num
end

function convergence_num(num) -- 数値を収束させる。
	num = math.floor(num)
	if num == 0 then return "nz" end
	local sign = ""
	if num < 0 then sign = "m" else sign = "p" end 
	num = math.abs(num) -- 絶対値化
	local temp = ""
	while num > 0 do
		temp = string.char(num % 256)..temp
		num = math.floor(num / 256)
	end
	return "n"..sign..string.char(temp:len())..temp
end

function expand_str(str_table) -- 文字列を展開させる。
	local length = head_chop(str_table)
	if length == 0 then return "" end
	local str = ""
	for i = 1, length do str = str .. head_letter(str_table) end
	return str
end

function convergence_str(str) -- 文字列を収束させる。
	return "s"..string.char(str:len())..str
end

function expand_bool(str_table) -- ブーリアンを展開させる。
	if head_letter(str_table) == "t" then return true else return false end
end

function convergence_bool(bool) -- ブーリアンを収束させる。
	if bool then return "bt" else return "bf" end
end

function expand_table(str_table) -- テーブルを展開させる。
	local t = {}
	local k
	local v
	while "e" ~= string.char(str_table[1]) do
		k = expand(str_table)
		v = expand(str_table)
		t[k] = v
	end
	head_letter(str_table)
	return t
end

function convergence_table(t) -- テーブルを収束させる。
	local str = ""
	for k, v in pairs(t) do str = str .. convergence(k) .. convergence(v) end
	return "t"..str.."e"
end