<commit_before>stead = {
	space_delim = ' ',
	scene_delim = '^^',
	delim = '|',
	call_top = 0,
	call_ctx = { txt = nil, self = nil },
	objects = {};
	next_dynamic = -1;
	max_dynamic = 32767;
	tables = {};
	functions = {};
	includes = {};
	tostr = tostring;
	tonum = tonumber;
	type = type;
	err = error;
	setmt = setmetatable;
	getmt = getmetatable;
	table = table;
	pairs = pairs;
	ipairs = ipairs;
	rawset = rawset;
	rawget = rawget;
	io = io;
	string = string;
	next = next;
	loadfile = loadfile;
	dofile = dofile;
	getinfo = debug.getinfo;
	__mod_hooks = {};
	files = {};
	busy = function() end;
}

local std = stead

function stead:abort()
	self.abort_cmd = true
end

local table = std.table
local pairs = std.pairs
local ipairs = std.ipairs
local string = std.string
local rawset = std.rawset
local rawget = std.rawget
local type = std.type
local io = std.io;

if _VERSION == "Lua 5.1" then
	std.eval = loadstring
	std.unpack = unpack
else
	std.eval = load
	std.unpack = table.unpack
	table.maxn = table_get_maxn
	string.gfind = string.gmatch
	math.mod = math.fmod
	math.log10 = function(a)
		return std.math.log(a, 10)
	end
end

local function __mod_callback_reg(f, hook, ...)
	if type(f) ~= 'function' then
		std.err ("Wrong parameter to mod_"..hook..".", 2);
	end
	if not std.__mod_hooks[hook] then
		std.__mod_hooks[hook] = {}
	end
	table.insert(std.__mod_hooks[hook], f);
--	f();
end

function std.mod_call(hook, ...)
	if not std.__mod_hooks[hook] then
		return
	end
	for k, v in ipairs(std.__mod_hooks[hook]) do
		local a, b = v(...)
		if a ~= nil or b ~= nil then
			return a, b
		end
	end
end

function std.mod_init(f, ...)
	__mod_callback_reg(f, 'init', ...)
	if std.initialized then -- require from game
		f(...)
	end
end

function std.mod_done(f, ...)
	__mod_callback_reg(f, 'done', ...)
end

function std.mod_start(f, ...)
	__mod_callback_reg(f, 'start', ...)
end

function std.mod_cmd(f, ...)
	__mod_callback_reg(f, 'cmd', ...)
end

function std.mod_save(f, ...)
	__mod_callback_reg(f, 'save', ...)
end

function std.hook(o, f)
	local ff
	if type(o) ~= 'function' then
		ff = function()
			return o
		end
	else
		ff = o
	end
	return function(...)
		return f(ff, ...)
	end
end

std.fmt = function(str, state)
	str = stead.obj.xref('<empty>', str)
	return str
end

local lua_keywords = {
	["and"] = true,
	["break"] = true,
	["do"] = true,
	["else"] = true,
	["elseif"] = true,
	["end"] = true,
	["false"] = true,
	["for"] = true,
	["function"] = true,
	["goto"] = true,
	["if"] = true,
	["in"] = true,
	["local"] = true,
	["nil"] = true,
	["not"] = true,
	["or"] = true,
	["repeat"] = true,
	["return"] = true,
	["then"] = true,
	["true"] = true,
	["until"] = true,
	["while"] = true,
}

std.setmt(stead, {
	__call = function(s, k)
		return std.ref(k)
	end;
})

function std.is_obj(v, t)
	if type(v) ~= 'table' then
		return false
	end
	return v['__'..(t or 'obj')..'_type']
end

function std.class(s, inh)
--	s.__parent = function(s)
--		return inh
	--	end;
	s.type = function(s, t)
		return std.is_obj(s, t)
	end;
	s.__call = function(s, ...)
		local a = { ... }
		if #a == 1 and type(a[1]) == 'string' then
			return std.ref(a[1])
		end
		return s:new(...)
	end;
	s.__tostring = function(self)
		if not std.is_obj(self) then
			local os = s.__tostring
			s.__tostring = nil
			local t = std.tostr(self)
			s.__tostring = os
			return t
		end
		return std.dispof(self)
	end;
	s.__dirty = function(s, v)
		local o = rawget(s, '__dirty_flag')
		if v ~= nil then
			if std.game then
				rawset(s, '__dirty_flag', v)
			end
			return s
		end
		return o
	end;
	s.__index = function(t, k)
		local ro = rawget(t, '__ro')
		local v
		if ro then
			v = rawget(ro, k)
		end
		if v == nil then
			return s[k]
		end
		if std.game and type(v) == 'table' then
			-- make rw if simple table
			if type(v.__dirty) ~= 'function' then
				t.__var[k] = true
				rawset(t, k, v)
				ro[k] = nil
			end
		end
		return v
	end;
	s.__newindex = function(t, k, v)
		local ro
		if std.is_obj(t) and type(k) == 'string' then
			ro = t.__ro
		end
		if not std.game and ro then
			rawset(ro, k, v)
			return
		end

		t:__dirty(true)
		if ro then
			if type(v) ~= 'function' or std.functions[v] then
				t.__var[k] = true
			else
				std.err("Wrong variable operation: "..std.tostr(k), 2)
			end
			ro[k] = nil
		end
		if std.is_obj(v, 'list') and std.is_obj(t) then
			v:attach(t)
		end
		rawset(t, k, v)
	end
	std.setmt(s, inh or { __call = s.__call })
	return s
end

function std.is_tag(n)
	return type(n) == 'string' and n:byte(1) == 0x23
end

std.list = std.class {
	__list_type = true;
	new = function(s, v)
		if type(v) ~= 'table' then
			std.err ("Wrong argument to std.list:"..std.tostr(v), 2)
		end
		if std.is_obj(v, 'list') then -- already list
			return v
		end
		v.__list = {} -- where is attached
		std.setmt(v, s)
		return v
	end;
	ini = function(s, o)
		for i = 1, #s do
			local k = s[i]
			s[i] = std.ref(k)
			if not std.is_obj(s[i]) then
				std.err("Wrong item in list: "..std.tostr(k), 2)
			end
			s:__attach(s[i])
		end
		if o then
			s:attach(o)
		end
	end;
	sort = function(s)
		std.table.sort(s, function(a, b)
			local p1 = std.tonum(a.pri) or 0
			local p2 = std.tonum(b.pri) or 0
			return p1 < p2
		end)
	end;
	display = function(s)
		local r
		for i = 1, #s do
			if r then
				r = r .. std.space_delim
			end
			local o = s[i]
			if not o:disabled() then
				local d = o:xref(std.call(s[i], 'dsc'))
				if type(d) == 'string' then
					r = (r or '').. d
				end
				if not o:closed() then
					d = o.obj:display()
					if type(d) == 'string' then
						r = (r or '') .. d
					end
				end
			end
		end
		return r
	end;
	disable = function(s)
		for i = 1, #s do
			s[i]:disable()
		end
	end;
	enable = function(s)
		for i = 1, #s do
			s[i]:enable()
		end
	end;
	close = function(s)
		for i = 1, #s do
			s[i]:close()
		end
	end;
	open = function(s)
		for i = 1, #s do
			s[i]:open()
		end
	end;
	attach = function(s, o) -- attach to object
		s:detach(o)
		table.insert(s.__list, o)
	end;
	detach = function(s, o) -- detach from object
		for i = 1, #s.__list do
			if s.__list[i] == o then
				table.remove(s.__list, i)
				break
			end
		end
	end;
	__attach = function(s, o) -- attach object to list
		s:__detach(o)
		table.insert(o.__list, s)
	end;
	__detach = function(s, o) -- detach object from list
		for i = 1, #o.__list do
			if o.__list[i] == s then
				table.remove(o.__list, i)
				break
			end
		end
	end;
	add = function(s, n, pos)
		if s:lookup(n) then
			return -- already here
		end
		if not pos then
			local o = std.ref(n)
			s:__dirty(true)
			s:__attach(o)
			table.insert(s, o)
			s:sort()
			return o
		end
		if type(pos) ~= 'number' then
			std.err("Wrong parameter to list.add:"..std.tostr(pos), 2)
		end
		if pos > #s then
			pos = #s
		elseif pos < 0 then
			pos = #s + pos + 1
		end
		if pos <= 0 then
			pos = 1
		end
		local o = std.ref(n)
		s:__dirty(true)
		s:__attach(o)
		if pos then
			table.insert(s, pos, o)
		else
			table.insert(s, o)
		end
		s:sort()
		return o
	end;
	for_each = function(s, fn, ...)
		if type(fn) ~= 'function' then
			std.err("Wrong parameter to list:for_each: "..std.tostr(fn), 2)
		end
		for i = 1, #s do
			local r, v = fn(s[i], ...)
			if r ~= nil or v ~= nil then
				return r, v
			end
		end
	end;
	lookup = function(s, n)
		local o, tag
		if std.is_tag(n) then
			tag = n
		else
			o = std.ref(n)
		end
		for i = 1, #s do
			if s[i] == o or (tag and s[i].tag == tag) then
				return s[i], i
			end
		end
	end;
--	seen = function(s, n)
--		for i = 1, #s do
--			local o = std.ref(s[i])
--			if std.dispof(o) == n then
--				return o, i
--			end
--		end
--	end;
	empty = function(s)
		return (#s == 0)
	end;
	zap = function(s) -- delete all objects
		local l = {}
		for i = 1, #s do
			table.insert(l, s[i])
		end
		for i = 1, #l do
			s:del(l[i])
		end
	end;
	del = function(s, n)
		local o, i = s:lookup(n)
		if i then
			s:__dirty(true)
			s:__detach(o)
			table.remove(s, i)
			s:sort()
			return o
		end
	end;
	save = function(s, fp, n)
		if not s:__dirty() then
			return
		end
		fp:write(string.format("%s = std.list { ", n))
		for i = 1, #s do
			local vv = std.deref(s[i])
			if not vv then
				std.err ("Can not do deref on: "..std.tostr(s[i]), 2)
			end
			if i ~= 1 then
				fp:write(string.format(", "))
			end
			if type(vv) == 'number' then
				fp:write(string.format("%d", vv))
			else
				fp:write(string.format("%q", vv))
			end
		end
		fp:write(" }:__dirty(true)\n")

	end;
}
std.save_var = function(vv, fp, n)
	if type(vv) == 'boolean' or type(vv) == 'number' then
		fp:write(string.format("%s = ", n))
		fp:write(std.tostr(vv)..'\n')
	elseif type(vv) == 'string' then
		fp:write(string.format("%s = ", n))
		fp:write(string.format("%q\n", vv))
	elseif type(vv) == 'function' then
		if std.functions[vv] and std.functions[vv] ~= n then
			local k = std.functions[vv]
			fp:write(string.format("%s = %s\n", n, k))
		else
			std.err("Can not save variable (function): "..n, 2)
		end
	elseif type(vv) == 'table' then
		if std.tables[vv] and std.tables[vv] ~= n then
			local k = std.tables[vv]
			fp:write(string.format("%s = %s\n", n, k))
		elseif std.is_obj(vv) then
			local d = std.deref(vv)
			if not d then
				std.err("Can not deref object:"..std.tostr(vv), 2)
			end
			fp:write(string.format("%s = ", n))
			if type(d) == 'string' then
				fp:write(string.format("std %q\n", d))
			else
				fp:write(string.format("std(%d)\n", d))
			end
		elseif type(vv.save) == 'function' then
			vv:save(fp, n)
		else
			fp:write(string.format("%s = %s\n", n,  std.dump(vv)))
--			std.save_table(vv, fp, n)
		end
	end
end

std.save_members = function(vv, fp, n)
	local l
	for k, v in pairs(vv) do
		l = nil
		if type(k) == 'number' then
			l = string.format("%s%s", n, std.varname(k))
			std.save_var(v, fp, l)
		elseif type(k) == 'string' then
			l = string.format("%s%s", n, std.varname(k))
			std.save_var(v, fp, l)
		end
	end
end

std.save_table = function(vv, fp, n)
	fp:write(string.format("%s = {}\n", n))
	std.save_members(vv, fp, n)
end

function std:reset() -- reset state
	self:done()
	self:init()
	std.dofile('main3.lua')
end

function std:load(fname) -- load save
	self:reset()
	std.ref 'game':ini(true)

	local f, err = std.loadfile(fname) -- load all diffs
	if not f then
		std.err(err, 2)
	end
	f();
	std.ref 'game':ini()
	return self.game:lastdisp()
end

function std:gamefile(fn, reset) -- load game file
	if type(fn) ~= 'string' then
		std.err("Wrong paramter to stead:file: "..std.tostr(f), 2)
	end
	if reset then
		std:reset()
		if fn ~= 'main3.lua' then
			std.startfile = fn -- another start file
		end
		std.ref 'game':ini()
		self.game.player:need_scene(true)
		return
	end
	std.__in_gamefile = true
	std.dofile(fn)
	std.__in_gamefile = false
	std.ref 'game':ini()
	table.insert(std.files, fn) -- remember it
end

function std:save(fp)
	local close
	if type(fp) == 'string' then
		fp = io.open(fp, "wb");
		if not fp then
			return nil, false -- can create file
		end
		close = true
	end
	local n
	if std.type(std.savename) == 'function' then
		n = std.savename()
	end
	if std.type(n) == 'string' then
		fp:write("-- $Name: "..n:gsub("\n","\\n").."$\n");
	end
	fp:write("local std = stead\n");
	-- reset
	if std.startfile then
		fp:write(string.format("std:reset(%q)\n", std.startfile))
	end
	-- files
	for i = 1, #std.files do
		fp:write(string.format("std:gamefile(%q)\n", std.files[i]))
	end

	local oo = std.objects

	std:busy(true)
	std.for_each_obj(function(v)
		if v.__dynamic then
			std:busy(true)
			v:save(fp, string.format("std(%s)", std.deref_str(v)))
		end
	end)

	std.mod_call('save', fp)

	std.for_each_obj(function(v)
		if not v.__dynamic then
			std:busy(true)
			v:save(fp, string.format("std(%s)", std.deref_str(v)))
		end
	end)
	if close then
		fp:flush();
		fp:close();
	end
	std:busy(false)
end

function std.for_each_obj(fn, ...)
	local oo = std.objects
	for k, v in pairs(oo) do
		local a, b = fn(v, ...)
		if a ~= nil and b ~= nil then
			return a, b
		end
	end
end

function std:init()
	std.obj { nam = '@',
		  {
			  iface = {
			  };
		  };
		  act = function(s, x)
			  local cmd = std.cmd_parse(x)
			  if s.iface[cmd[1]] then
				  return std.call(s.iface, std.unpack(cmd))
			  end
			  std.err ('Undefined @ act: '..std.tostr(x), 2)
	end; }

	std.world { nam = 'game', player = 'player', codepage = 'UTF-8' }
	std.room { nam = 'main' }<commit_msg>for_each_obj fix<commit_after>stead = {
	space_delim = ' ',
	scene_delim = '^^',
	delim = '|',
	call_top = 0,
	call_ctx = { txt = nil, self = nil },
	objects = {};
	next_dynamic = -1;
	max_dynamic = 32767;
	tables = {};
	functions = {};
	includes = {};
	tostr = tostring;
	tonum = tonumber;
	type = type;
	err = error;
	setmt = setmetatable;
	getmt = getmetatable;
	table = table;
	pairs = pairs;
	ipairs = ipairs;
	rawset = rawset;
	rawget = rawget;
	io = io;
	string = string;
	next = next;
	loadfile = loadfile;
	dofile = dofile;
	getinfo = debug.getinfo;
	__mod_hooks = {};
	files = {};
	busy = function() end;
}

local std = stead

function stead:abort()
	self.abort_cmd = true
end

local table = std.table
local pairs = std.pairs
local ipairs = std.ipairs
local string = std.string
local rawset = std.rawset
local rawget = std.rawget
local type = std.type
local io = std.io;

if _VERSION == "Lua 5.1" then
	std.eval = loadstring
	std.unpack = unpack
else
	std.eval = load
	std.unpack = table.unpack
	table.maxn = table_get_maxn
	string.gfind = string.gmatch
	math.mod = math.fmod
	math.log10 = function(a)
		return std.math.log(a, 10)
	end
end

local function __mod_callback_reg(f, hook, ...)
	if type(f) ~= 'function' then
		std.err ("Wrong parameter to mod_"..hook..".", 2);
	end
	if not std.__mod_hooks[hook] then
		std.__mod_hooks[hook] = {}
	end
	table.insert(std.__mod_hooks[hook], f);
--	f();
end

function std.mod_call(hook, ...)
	if not std.__mod_hooks[hook] then
		return
	end
	for k, v in ipairs(std.__mod_hooks[hook]) do
		local a, b = v(...)
		if a ~= nil or b ~= nil then
			return a, b
		end
	end
end

function std.mod_init(f, ...)
	__mod_callback_reg(f, 'init', ...)
	if std.initialized then -- require from game
		f(...)
	end
end

function std.mod_done(f, ...)
	__mod_callback_reg(f, 'done', ...)
end

function std.mod_start(f, ...)
	__mod_callback_reg(f, 'start', ...)
end

function std.mod_cmd(f, ...)
	__mod_callback_reg(f, 'cmd', ...)
end

function std.mod_save(f, ...)
	__mod_callback_reg(f, 'save', ...)
end

function std.hook(o, f)
	local ff
	if type(o) ~= 'function' then
		ff = function()
			return o
		end
	else
		ff = o
	end
	return function(...)
		return f(ff, ...)
	end
end

std.fmt = function(str, state)
	str = stead.obj.xref('<empty>', str)
	return str
end

local lua_keywords = {
	["and"] = true,
	["break"] = true,
	["do"] = true,
	["else"] = true,
	["elseif"] = true,
	["end"] = true,
	["false"] = true,
	["for"] = true,
	["function"] = true,
	["goto"] = true,
	["if"] = true,
	["in"] = true,
	["local"] = true,
	["nil"] = true,
	["not"] = true,
	["or"] = true,
	["repeat"] = true,
	["return"] = true,
	["then"] = true,
	["true"] = true,
	["until"] = true,
	["while"] = true,
}

std.setmt(stead, {
	__call = function(s, k)
		return std.ref(k)
	end;
})

function std.is_obj(v, t)
	if type(v) ~= 'table' then
		return false
	end
	return v['__'..(t or 'obj')..'_type']
end

function std.class(s, inh)
--	s.__parent = function(s)
--		return inh
	--	end;
	s.type = function(s, t)
		return std.is_obj(s, t)
	end;
	s.__call = function(s, ...)
		local a = { ... }
		if #a == 1 and type(a[1]) == 'string' then
			return std.ref(a[1])
		end
		return s:new(...)
	end;
	s.__tostring = function(self)
		if not std.is_obj(self) then
			local os = s.__tostring
			s.__tostring = nil
			local t = std.tostr(self)
			s.__tostring = os
			return t
		end
		return std.dispof(self)
	end;
	s.__dirty = function(s, v)
		local o = rawget(s, '__dirty_flag')
		if v ~= nil then
			if std.game then
				rawset(s, '__dirty_flag', v)
			end
			return s
		end
		return o
	end;
	s.__index = function(t, k)
		local ro = rawget(t, '__ro')
		local v
		if ro then
			v = rawget(ro, k)
		end
		if v == nil then
			return s[k]
		end
		if std.game and type(v) == 'table' then
			-- make rw if simple table
			if type(v.__dirty) ~= 'function' then
				t.__var[k] = true
				rawset(t, k, v)
				ro[k] = nil
			end
		end
		return v
	end;
	s.__newindex = function(t, k, v)
		local ro
		if std.is_obj(t) and type(k) == 'string' then
			ro = t.__ro
		end
		if not std.game and ro then
			rawset(ro, k, v)
			return
		end

		t:__dirty(true)
		if ro then
			if type(v) ~= 'function' or std.functions[v] then
				t.__var[k] = true
			else
				std.err("Wrong variable operation: "..std.tostr(k), 2)
			end
			ro[k] = nil
		end
		if std.is_obj(v, 'list') and std.is_obj(t) then
			v:attach(t)
		end
		rawset(t, k, v)
	end
	std.setmt(s, inh or { __call = s.__call })
	return s
end

function std.is_tag(n)
	return type(n) == 'string' and n:byte(1) == 0x23
end

std.list = std.class {
	__list_type = true;
	new = function(s, v)
		if type(v) ~= 'table' then
			std.err ("Wrong argument to std.list:"..std.tostr(v), 2)
		end
		if std.is_obj(v, 'list') then -- already list
			return v
		end
		v.__list = {} -- where is attached
		std.setmt(v, s)
		return v
	end;
	ini = function(s, o)
		for i = 1, #s do
			local k = s[i]
			s[i] = std.ref(k)
			if not std.is_obj(s[i]) then
				std.err("Wrong item in list: "..std.tostr(k), 2)
			end
			s:__attach(s[i])
		end
		if o then
			s:attach(o)
		end
	end;
	sort = function(s)
		std.table.sort(s, function(a, b)
			local p1 = std.tonum(a.pri) or 0
			local p2 = std.tonum(b.pri) or 0
			return p1 < p2
		end)
	end;
	display = function(s)
		local r
		for i = 1, #s do
			if r then
				r = r .. std.space_delim
			end
			local o = s[i]
			if not o:disabled() then
				local d = o:xref(std.call(s[i], 'dsc'))
				if type(d) == 'string' then
					r = (r or '').. d
				end
				if not o:closed() then
					d = o.obj:display()
					if type(d) == 'string' then
						r = (r or '') .. d
					end
				end
			end
		end
		return r
	end;
	disable = function(s)
		for i = 1, #s do
			s[i]:disable()
		end
	end;
	enable = function(s)
		for i = 1, #s do
			s[i]:enable()
		end
	end;
	close = function(s)
		for i = 1, #s do
			s[i]:close()
		end
	end;
	open = function(s)
		for i = 1, #s do
			s[i]:open()
		end
	end;
	attach = function(s, o) -- attach to object
		s:detach(o)
		table.insert(s.__list, o)
	end;
	detach = function(s, o) -- detach from object
		for i = 1, #s.__list do
			if s.__list[i] == o then
				table.remove(s.__list, i)
				break
			end
		end
	end;
	__attach = function(s, o) -- attach object to list
		s:__detach(o)
		table.insert(o.__list, s)
	end;
	__detach = function(s, o) -- detach object from list
		for i = 1, #o.__list do
			if o.__list[i] == s then
				table.remove(o.__list, i)
				break
			end
		end
	end;
	add = function(s, n, pos)
		if s:lookup(n) then
			return -- already here
		end
		if not pos then
			local o = std.ref(n)
			s:__dirty(true)
			s:__attach(o)
			table.insert(s, o)
			s:sort()
			return o
		end
		if type(pos) ~= 'number' then
			std.err("Wrong parameter to list.add:"..std.tostr(pos), 2)
		end
		if pos > #s then
			pos = #s
		elseif pos < 0 then
			pos = #s + pos + 1
		end
		if pos <= 0 then
			pos = 1
		end
		local o = std.ref(n)
		s:__dirty(true)
		s:__attach(o)
		if pos then
			table.insert(s, pos, o)
		else
			table.insert(s, o)
		end
		s:sort()
		return o
	end;
	for_each = function(s, fn, ...)
		if type(fn) ~= 'function' then
			std.err("Wrong parameter to list:for_each: "..std.tostr(fn), 2)
		end
		for i = 1, #s do
			local r, v = fn(s[i], ...)
			if r ~= nil or v ~= nil then
				return r, v
			end
		end
	end;
	lookup = function(s, n)
		local o, tag
		if std.is_tag(n) then
			tag = n
		else
			o = std.ref(n)
		end
		for i = 1, #s do
			if s[i] == o or (tag and s[i].tag == tag) then
				return s[i], i
			end
		end
	end;
--	seen = function(s, n)
--		for i = 1, #s do
--			local o = std.ref(s[i])
--			if std.dispof(o) == n then
--				return o, i
--			end
--		end
--	end;
	empty = function(s)
		return (#s == 0)
	end;
	zap = function(s) -- delete all objects
		local l = {}
		for i = 1, #s do
			table.insert(l, s[i])
		end
		for i = 1, #l do
			s:del(l[i])
		end
	end;
	del = function(s, n)
		local o, i = s:lookup(n)
		if i then
			s:__dirty(true)
			s:__detach(o)
			table.remove(s, i)
			s:sort()
			return o
		end
	end;
	save = function(s, fp, n)
		if not s:__dirty() then
			return
		end
		fp:write(string.format("%s = std.list { ", n))
		for i = 1, #s do
			local vv = std.deref(s[i])
			if not vv then
				std.err ("Can not do deref on: "..std.tostr(s[i]), 2)
			end
			if i ~= 1 then
				fp:write(string.format(", "))
			end
			if type(vv) == 'number' then
				fp:write(string.format("%d", vv))
			else
				fp:write(string.format("%q", vv))
			end
		end
		fp:write(" }:__dirty(true)\n")

	end;
}
std.save_var = function(vv, fp, n)
	if type(vv) == 'boolean' or type(vv) == 'number' then
		fp:write(string.format("%s = ", n))
		fp:write(std.tostr(vv)..'\n')
	elseif type(vv) == 'string' then
		fp:write(string.format("%s = ", n))
		fp:write(string.format("%q\n", vv))
	elseif type(vv) == 'function' then
		if std.functions[vv] and std.functions[vv] ~= n then
			local k = std.functions[vv]
			fp:write(string.format("%s = %s\n", n, k))
		else
			std.err("Can not save variable (function): "..n, 2)
		end
	elseif type(vv) == 'table' then
		if std.tables[vv] and std.tables[vv] ~= n then
			local k = std.tables[vv]
			fp:write(string.format("%s = %s\n", n, k))
		elseif std.is_obj(vv) then
			local d = std.deref(vv)
			if not d then
				std.err("Can not deref object:"..std.tostr(vv), 2)
			end
			fp:write(string.format("%s = ", n))
			if type(d) == 'string' then
				fp:write(string.format("std %q\n", d))
			else
				fp:write(string.format("std(%d)\n", d))
			end
		elseif type(vv.save) == 'function' then
			vv:save(fp, n)
		else
			fp:write(string.format("%s = %s\n", n,  std.dump(vv)))
--			std.save_table(vv, fp, n)
		end
	end
end

std.save_members = function(vv, fp, n)
	local l
	for k, v in pairs(vv) do
		l = nil
		if type(k) == 'number' then
			l = string.format("%s%s", n, std.varname(k))
			std.save_var(v, fp, l)
		elseif type(k) == 'string' then
			l = string.format("%s%s", n, std.varname(k))
			std.save_var(v, fp, l)
		end
	end
end

std.save_table = function(vv, fp, n)
	fp:write(string.format("%s = {}\n", n))
	std.save_members(vv, fp, n)
end

function std:reset() -- reset state
	self:done()
	self:init()
	std.dofile('main3.lua')
end

function std:load(fname) -- load save
	self:reset()
	std.ref 'game':ini(true)

	local f, err = std.loadfile(fname) -- load all diffs
	if not f then
		std.err(err, 2)
	end
	f();
	std.ref 'game':ini()
	return self.game:lastdisp()
end

function std:gamefile(fn, reset) -- load game file
	if type(fn) ~= 'string' then
		std.err("Wrong paramter to stead:file: "..std.tostr(f), 2)
	end
	if reset then
		std:reset()
		if fn ~= 'main3.lua' then
			std.startfile = fn -- another start file
		end
		std.ref 'game':ini()
		self.game.player:need_scene(true)
		return
	end
	std.__in_gamefile = true
	std.dofile(fn)
	std.__in_gamefile = false
	std.ref 'game':ini()
	table.insert(std.files, fn) -- remember it
end

function std:save(fp)
	local close
	if type(fp) == 'string' then
		fp = io.open(fp, "wb");
		if not fp then
			return nil, false -- can create file
		end
		close = true
	end
	local n
	if std.type(std.savename) == 'function' then
		n = std.savename()
	end
	if std.type(n) == 'string' then
		fp:write("-- $Name: "..n:gsub("\n","\\n").."$\n");
	end
	fp:write("local std = stead\n");
	-- reset
	if std.startfile then
		fp:write(string.format("std:reset(%q)\n", std.startfile))
	end
	-- files
	for i = 1, #std.files do
		fp:write(string.format("std:gamefile(%q)\n", std.files[i]))
	end

	local oo = std.objects

	std:busy(true)
	std.for_each_obj(function(v)
		if v.__dynamic then
			std:busy(true)
			v:save(fp, string.format("std(%s)", std.deref_str(v)))
		end
	end)

	std.mod_call('save', fp)

	std.for_each_obj(function(v)
		if not v.__dynamic then
			std:busy(true)
			v:save(fp, string.format("std(%s)", std.deref_str(v)))
		end
	end)
	if close then
		fp:flush();
		fp:close();
	end
	std:busy(false)
end

function std.for_each_obj(fn, ...)
	local oo = std.objects
	for k, v in pairs(oo) do
		if std.is_obj(v) then
			local a, b = fn(v, ...)
			if a ~= nil and b ~= nil then
				return a, b
			end
		end
	end
end

function std:init()
	std.obj { nam = '@',
		  {
			  iface = {
			  };
		  };
		  act = function(s, x)
			  local cmd = std.cmd_parse(x)
			  if s.iface[cmd[1]] then
				  return std.call(s.iface, std.unpack(cmd))
			  end
			  std.err ('Undefined @ act: '..std.tostr(x), 2)
	end; }

	std.world { nam = 'game', player = 'player', codepage = 'UTF-8' }
	std.room { nam = 'main' }
{'fraction_non_alphanumeric': 0.17284397762485412, 'fraction_numerical': 0.0050706265845708075, 'mean_word_length': 2.7577498865870256, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1134357', 'n_tokens_mistral': 11719, 'n_tokens_neox': 10568, 'n_words': 2873}