1-- add this: reuire "dbg"
2-- in your project
3-- for debug tools
4require "input"
5
6local function _xref_escape(n)
7	local delim = ':'
8	if stead.api_atleast(1, 2, 2) then
9		delim = stead.delim;
10	end
11	if xact then
12		n = n:gsub("\\?[\\"..delim.."]", { [delim] = "\\"..delim } )
13	end
14	return n
15end
16
17local function ordered_n(t)
18	local ordered = {};
19	local max = 0
20	for i, v in stead.pairs(t) do
21		local o = { k = i; v = v };
22		stead.table.insert(ordered, o);
23		max = max + 1;
24	end
25	stead.table.sort(ordered, function(a, b)
26		if isObject(a.v) and not isObject(b.v) then
27			return true
28		end
29		if not isObject(a.v) and isObject(b.v) then
30			return false
31		end
32		if isObject(a.v) and isObject(b.v) then
33			local n = stead.call(a.v, 'nam');
34			local m = stead.call(b.v, 'nam');
35			if stead.type(n) ~= 'string' and stead.type(m) ~= 'string' then
36				return false
37			end
38			if stead.type(n) ~= 'string' then
39				return true
40			end
41			if stead.type(m) ~= 'string' then
42				return false
43			end
44			if n == m then
45				local o1, o2
46				o1 = stead.deref(a.v)
47				o2 = stead.deref(b.v)
48				if stead.type(o1) == 'string' and stead.type(o2) == 'string' then
49					return o1 < o2
50				end
51			end
52			return n < m;
53		end
54		return false
55	end)
56	ordered.i = 1;
57	ordered.max = max;
58	return ordered;
59end
60
61local function snext(t, k)
62	local v
63	if not k then
64		k = ordered_n(t);
65	end
66	if k.i > k.max then
67		return nil
68	end
69	v = k[k.i]
70	k.i = k.i + 1
71	return k, v.v, v.k;
72end
73
74local function spairs(s, var)
75	return snext, s, nil;
76end
77
78function dbg_disp_obj()
79	local v = obj {
80		nam = 'disp',
81		act = true,
82		dsc = function(s)
83			local r = s._txt
84			s._txt = nil;
85			return r
86		end;
87		save = function(self, name, h, need)
88			if need then
89				h:write(stead.string.format("%s  = dbg_disp_obj();\n", name));
90			end
91			stead.savemembers(h, self, name, false);
92		end
93	}
94	return v;
95end
96
97dbg_dump_obj = function(w)
98	w = stead.ref(w)
99
100	if stead.type(w) ~= 'table' then
101		seen('disp')._txt = '^^No such object.';
102		return true
103	end
104	local n
105	local rc = ''
106	for i, o in stead.pairs(w) do
107		local t = stead.tostring(o);
108		if t == i then
109			t = stead.tostr(o);
110		end
111		if t then
112			if rc ~='' then rc = rc..'^' end
113			local n = '';
114			if isObject(o) then
115				n = stead.call(o, 'nam');
116				if stead.type(n) ~= 'string' then n = '' else n = ' : '..n; end
117			end
118			rc = stead.cat(rc, stead.par(' ', stead.tostr(i)..' : '..t..n));
119		end
120	end
121	seen('disp')._txt = stead.cat('^^', rc)
122	return true;
123end
124
125dbg_dump_globals = function()
126	local rc=''
127	if stead.type(variables) ~= 'table' then
128		return
129	end
130	for i, o in stead.ipairs(variables) do
131		local v = stead.rawget(_G, o);
132		local t = stead.tostring(v);
133		if t then
134			if rc ~='' then rc = rc..'^' end
135			rc = stead.cat(rc, stead.par(' ', stead.tostr(o)..' : '..t));
136		end
137	end
138	seen('disp')._txt = stead.cat('^^', rc)
139	return true;
140end
141
142dbg_here = function()
143	return debug_tool._here
144end
145
146dbg_list_objects = function()
147	local dis = function(o)
148		if isDisabled(o) then
149			return ", disabled"
150		end
151		return ''
152	end
153	local rc = stead.par(' ', 'Room:'..stead.tostr(stead.deref(dbg_here())),
154			'Nam:'..stead.tostr(stead.call(dbg_here(),'nam')));
155	for i, o in stead.opairs(objs(dbg_here())) do
156		rc = rc..'^';
157		o = stead.ref(o)
158		rc = stead.cat(rc, stead.par(' ', 'Id: '..stead.tostr(o.id)..', '..
159			stead.tostr(stead.deref(o))..': '..stead.tostr(stead.call(o, 'nam'))..dis(o)));
160	end
161--	seen('disp')._txt = rc
162	return rc
163end
164
165dbg_list_inv = function()
166	local rc = ''
167	local dis = function(o)
168		if isDisabled(o) then
169			return ", disabled"
170		end
171		return ''
172	end
173
174	local tak = function(o)
175		if taken(o) then
176			return ", taken"
177		end
178		return ''
179	end
180
181	for i, o in stead.opairs(inv()) do
182		if rc ~='' then rc = rc..'^' end
183		o = stead.ref(o)
184		rc = stead.cat(rc, stead.par(' ', 'Id: '..stead.tostr(o.id)..', '..
185			stead.tostr(stead.deref(o))..': '..stead.tostr(stead.call(o, 'nam'))..dis(o)..tak(o)));
186	end
187	if rc == '' then return end
188--	seen('disp')._txt = rc
189	return rc
190end
191
192dbg_execute_cmd = room {
193	nam = "Execute Lua code",
194	debug = true,
195	pic = true,
196	system_type = true,
197	forcedsc = true,
198	dsc = "Enter Lua code here to exec.",
199	inp_enter = function(s)
200		if stead.type(s.obj[1]._txt) == 'string' then
201			local f = stead.eval(s.obj[1]._txt);
202			if f then
203				seen('disp')._txt = stead.cat('^^', f());
204				return true
205			end
206			seen('disp')._txt = "^^Error in exec.";
207			return true
208		end
209		return stead.back();
210	end,
211	obj = { inp('inp', '{Enter cmd}: ', 'return "Hello World!"'),
212		obj { nam = 'Back', dsc = '^{Back}', act = code [[ stead.back() ]] },
213		dbg_disp_obj(),
214	},
215	exit = function(s)
216		s.obj[1]:state(false)
217	end;
218}
219
220dbg_dump_object = room {
221	nam = "Dump object",
222	debug = true,
223	pic = true,
224	system_type = true,
225	forcedsc = true,
226	dsc = "Enter object name here to dump.",
227	inp_enter = function(s)
228		local w = s.obj[1]._txt
229		if stead.type(w) == 'string' then
230			if not stead.ref(w) then w = objs(dbg_here()):srch(w); end
231			return dbg_dump_obj(w);
232		end
233		return stead.back();
234	end,
235	obj = { inp('inp', '{Enter object}: ', 'main'),
236		obj{nam = 'Here', dsc = '^{Dump here}', act = code[[ return dbg_dump_obj(dbg_here())]]},
237		obj{nam = 'Player',dsc =  '^{Dump player}', act = code[[ return dbg_dump_obj(stead.me())]]},
238		obj{nam = 'Lifes', dsc = '^{Dump lifes}', act = code[[ return dbg_dump_obj(debug_tool.lifes)]]},
239		obj{nam = 'Ways', dsc = '^{Dump ways}', act = code[[ return dbg_dump_obj(ways(dbg_here()))]]},
240		obj{nam = 'Globals', dsc = '^{Dump globals}', act = code [[return dbg_dump_globals()]] },
241		obj{nam = 'Back', dsc = '^{Back}', act = code [[ return stead.back() ]] },
242		dbg_disp_obj() },
243	exit = function(s)
244		s.obj[1]:state(false)
245	end;
246}
247
248dbg_choose_location = dlg {
249	debug = true,
250	pic = true,
251	system_type = true,
252	forcedsc = true,
253	nam = 'Go to',
254	dsc = 'Select location.',
255	gen = function(s)
256		objs(s):zap();
257		for k, v, kk in spairs(_G) do
258			if isRoom(v) and not v.debug then
259				local n = stead.tostr(stead.call(v, 'nam'));
260				local o = kk;
261				if stead.type(o) == 'string' then
262					n = n..' : '..o;
263					n = _xref_escape(n);
264					put(phr(n, true, [[timer:set(debug_tool._timer); game.lifes:cat(debug_tool.lifes); return stead.walk(]]..o..[[)]]), s);
265				end
266			end
267		end
268		put (phr('Back',true, 'return stead.back()'), s)
269	end
270}
271
272dbg_choose_object = dlg {
273	debug = true,
274	pic = true,
275	system_type = true,
276	forcedsc = true,
277	nam = 'Get object',
278	dsc = 'Select object to get.',
279	gen = function(s)
280		objs(s):zap();
281		for k, v, kk in spairs(_G) do
282			if isObject(v) and not isPhrase(v) and not isRoom(v) and not isPlayer(v) and not v.debug and not have(v) and not isStatus(v) then
283				local n = stead.tostr(stead.call(v, 'nam'));
284				local o = kk;
285				if stead.type(o) == 'string' then
286					n = n..' : '..o;
287					n = _xref_escape(n);
288					put(phr(n, true, o..':enable(); return take('..o..')'), s);
289				end
290			end
291		end
292		put (phr('Back',true, 'return stead.back()'), s)
293	end
294}
295
296dbg_drop_object = dlg {
297	debug = true,
298	pic = true,
299	forcedsc = true,
300	system_type = true,
301	nam = 'Drop object',
302	dsc = 'Select object to drop.',
303	gen = function(s)
304		objs(s):zap();
305		for k, v in stead.ipairs(inv()) do
306			v = stead.ref(v);
307			if not v.debug then
308				local n = stead.tostr(stead.call(v, 'nam'));
309				local o = stead.deref(v);
310				if stead.type(o) == 'string' then
311					n = n..' : '..o;
312					n = _xref_escape(n);
313					put (phr(n, true, o..':enable(); drop('..o..','..stead.deref(dbg_here())..')'), s)
314				end
315			end
316		end
317		put (phr('Back', true, 'return stead.back()'), s)
318	end
319}
320
321function dbg_exit()
322	local r
323	if not stead.api_atleast(1, 2, 0) then
324		r = stead.call(dbg_here(), 'dsc');
325	end
326	game.lifes:cat(debug_tool.lifes);
327	timer:set(debug_tool._timer);
328	return stead.par ('^^', stead.back(), r);
329end
330
331debug_dlg = dlg {
332	debug = true,
333	pic = true,
334	system_type = true,
335	forcedsc = true,
336	nam = 'Debug Tool',
337	dsc = 'Select tool.',
338	obj = {
339		phr('Go to location...', true, [[pon(); dbg_choose_location:gen(); return stead.walk('dbg_choose_location')]]),
340		phr('Get object...', true, [[pon(); dbg_choose_object:gen(); return stead.walk('dbg_choose_object')]]),
341		phr('Put object...', true, [[pon(); dbg_drop_object:gen(); return stead.walk('dbg_drop_object')]]),
342		phr('Current scene...', true, [[pon(); return dbg_list_objects();]]),
343		phr('Inventory...', true, [[pon(); return dbg_list_inv();]]),
344		phr('Dump object...', true, [[pon(); return stead.walk(dbg_dump_object);]]),
345		phr('Exec Lua string...', true, [[pon(); return stead.walk('dbg_execute_cmd')]]),
346		phr('Exit',true , [[pon(); return dbg_exit()]]),
347	},
348};
349
350debug_tool = menu {
351	debug = true,
352	system_type = true,
353	forcedsc = true,
354	nam = txtb('debug'),
355	lifes = list {},
356	inv = function(s)
357		if stead.here().debug then
358			return nil, true --nothing todo
359		end
360		debug_dlg.__from__ = stead.here();
361		s._timer = timer:get();
362		timer:stop();
363		s.lifes:zap();
364		s.lifes:cat(game.lifes);
365		game.lifes:zap();
366		s._here = stead.here();
367		stead.me().where = 'debug_dlg'; -- force to go
368		return stead.walk(self.where);
369	end,
370};
371
372game.action = stead.hook(game.action,
373function (f, s, cmd, ...)
374	if cmd == 'use_debug' then
375		return debug_tool:inv()
376	elseif cmd == 'exit_debug' then
377		stead.me().where = 'debug_dlg';
378		dbg_execute_cmd.obj[1]:state(false)
379		dbg_dump_object.obj[1]:state(false)
380		return dbg_exit()
381	end
382	return f(s, cmd, ...)
383end)
384
385stead.module_init(function()
386	input.key = stead.hook(input.key,
387	function(f, s, down, key, ...)
388		if down and key == 'f7' then
389			if stead.here().debug then
390				return 'exit_debug'
391			else
392				return 'use_debug'
393			end
394		end
395
396		return f(s, down, key, ...)
397	end)
398	putf('debug_tool', stead.me());
399end)
400
401
402-- vim:ts=4
403