1--$Name:Miner Bold$
2--$Version:1.4$
3--$Author:Peter Kosyh$
4
5instead_version "2.0.0"
6TIMER = 85
7FAST_TIMER = 30
8
9require "sprites"
10require "sound"
11require "timer"
12require "kbd"
13require "click"
14
15-- require "prefs"
16
17sprites = {}
18sprites_small = {}
19
20history = {}
21
22sounds = {}
23global {
24	nr_level = 0;
25	selected_level = 0;
26	nr_score = 0;
27	map = {};
28	prefs = { }
29};
30
31prefs.stat = {}
32prefs.maps_stat = {}
33
34SDIE = 1
35SFALL = 2
36SCLICK = 3
37SLEVELIN = 4
38STRILL = 5
39SPHASER = 6
40
41load_sounds = function()
42	sounds[SDIE] = sound.load "snd/explode.ogg"
43	sounds[SFALL] = sound.load "snd/fall.ogg"
44	sounds[SCLICK] = sound.load "snd/click.ogg"
45	sounds[SLEVELIN] = sound.load "snd/levelin.ogg"
46	sounds[STRILL] = sound.load "snd/trill.ogg"
47	sounds[SPHASER] = sound.load "snd/phaser.ogg"
48end
49
50load_sprites = function()
51	local i
52	local files = {
53		'00.png',
54		'01.png',
55		'02.png',
56		'03.png',
57		'04.png',
58		'05.png',
59		'06.png',
60		'07.png',
61		'02.png',
62		'02.png',
63		'03.png',
64		'03.png',
65		'12.png',
66		'13.png',
67		'14.png',
68		'15.png',
69		'16.png',
70		'17.png',
71		'18.png',
72		'19.png',
73		'20.png',
74		'21.png',
75	}
76	for i=1, #files do
77		local s = sprite.load("gfx/"..files[i])
78		if i <= 8 then
79			sprites_small[i] = s
80		end
81		sprites[i] = sprite.scale(s, 2.0, 2.0, false)
82--		sprite.free(s)
83	end
84	fn = sprite.font("gfx/font.ttf", 16);
85	fn2 = sprite.font("gfx/font.ttf", 26);
86	tfn = sprite.font("gfx/font.ttf", 12);
87	press_any_key = sprite.text(tfn, _("press:PRESS ANY KEY"), 'red', 1)
88	press_enter = sprite.text(fn, _("press_enter:PRESS ENTER"), 'red', 1)
89	select_maps_spr = sprite.text(fn2, _("banks:SELECT GAME"), 'red', 1)
90	la_spr = sprite.load("gfx/la.png")
91	ra_spr = sprite.load("gfx/ra.png")
92end
93
94BEMPTY = 0
95BGRASS = 1
96BSTONE = 2
97BSTONE_LAZY = BSTONE + 128
98BGOLD = 3
99BHUMAN = 4
100BBLOCK = 5
101BHEART = 6
102BFLY = 7
103
104BSTONE2 = BSTONE * 2 + 4
105BSTONE3 = BSTONE2 + 1
106
107BGOLD2 = BGOLD * 2 + 4
108BGOLD3 = BGOLD2 + 1
109
110global {
111	scatter_dir = -1;
112	enemies = {};
113}
114
115level_store = function()
116	local map2char = {
117		[0] = ' ',
118		[1] = ':',
119		[2] = '@',
120		[3] = '$',
121		[4] = '+',
122		[5] = '#',
123		[6] = '&',
124		[7] = '%',
125	}
126	local x
127	local y
128	for y = 0, 15 do
129		local line = ''
130		for x = 0, 15 do
131			local c = cell_get(x * 2, y * 2)
132			c = map2char[c];
133			line = line .. c;
134		end
135		maps[nr_level * 16 + y + 1] = line
136	end
137end
138
139level_load = function()
140	enemies = {};
141	history = {}
142	scatter_dir = -1
143
144	local char2map = {
145		[' '] = 0,
146		[':'] = 1,
147		['@'] = 2,
148		['$'] = 3,
149		['+'] = 4,
150		['#'] = 5,
151		['&'] = 6,
152		['%'] = 7,
153	}
154	local line = nr_level * 16
155	local x
156	local y
157	local was_human
158	for y = 1, 16 do
159		map[y] = {}
160		local row = maps[line + y]
161		for x = 1, 16 do
162			local c = string.sub(row, x, x);
163			c = char2map[c]
164			if c == BHUMAN and was_human then
165				c = BEMPTY
166			end
167			map[y][x] = c
168			if c == BHUMAN then
169				was_human = true
170				player_x = (x - 1) * 2
171				player_y = (y - 1) * 2
172				player_movex = 0;
173				player_movey = 0;
174			elseif c >= BHEART then --
175				stead.table.insert(enemies, { x = (x - 1) * 2, y = (y - 1) * 2, dx = 0, dy = 0 })
176			end
177		end
178	end
179	if not was_human then
180		player_x = 0
181		player_y = 0
182		player_movex = 0;
183		player_movey = 0;
184		map[1][1] = BHUMAN
185	end
186end
187
188scr_w = 512
189scr_h = 512
190
191level_render = function(where, offset)
192	if not offset then offset = 0 end
193	local y
194	local x
195	for y = 1,16 do
196		local yy = 32 * (y - 1) + offset
197		if yy >= scr_h then
198			return
199		end
200		if yy >= 0 then
201			local l = map[y]
202			for x = 1, 16 do
203				local c = l[x] + 1
204				sprite.copy(sprites[c], where, 32 * (x - 1), yy)
205			end
206		end
207	end
208end
209level_map = function(where, ox, oy)
210	local y
211	local x
212	for y = 1,16 do
213		local yy = 16 * (y - 1)
214		local l = map[y]
215		for x = 1, 16 do
216			local c = l[x] + 1
217			sprite.copy(sprites_small[c], where, 16 * (x - 1) + ox, yy + oy)
218		end
219	end
220end
221
222keys = {}
223key_empty = function()
224	fingers = {}
225	keys = {}
226	key_any, key_esc, key_demo, key_return, key_edit = false, false, false, false, false
227end
228
229fingers = {}
230touch_stamp = 0;
231touch_num = 0
232touch_max = 0
233if stead.finger_pos then
234	require "finger"
235	game.finger = function(s, press, fid, x, y)
236		use_fingers = true
237		if press then
238			if stead.ticks() - touch_stamp > 200 then
239				touch_num = 0
240				touch_stamp = stead.ticks()
241			end
242			touch_num = touch_num + 1
243			touch_max = #finger:list()
244		else
245			local tm = touch_max
246			touch_num = 0
247			touch_stamp = 0
248			touch_max = 0
249			if tm >=4 and edit_mode then
250				local x, y
251				for y=0, 15 do
252					for x=0, 15 do
253						sprite_draw(x * 2, y * 2, BEMPTY);
254						cell_set(x * 2, y * 2, BEMPTY);
255					end
256				end
257				return
258			elseif tm >= 3 then
259				key_edit = true
260				return
261			end
262		end
263		if touch_num >= 3 then
264			key_esc = true
265			return
266		end
267		if press and x > scr_w / 3 and x < scr_w * 2 / 3 and not edit_mode and touch_max == 1 then
268			key_return = press
269			key_any = press
270		end
271		if press then
272			stead.table.insert(fingers, 1, { id = fid, x = x, y = y })
273		else
274			local k,v
275			for k,v in ipairs(fingers) do
276				if v.id == fid then
277					stead.table.remove(fingers, k)
278					break
279				end
280			end
281			if #fingers == 0 then
282				key_empty()
283			end
284		end
285	end
286end
287
288function check_fingers()
289	if not use_fingers then
290		return
291	end
292	keys = {}
293
294	local fng = finger:list()
295	local k, v
296	if #fingers == 0 then
297		return
298	end
299	local V
300	for k,v in ipairs(fng) do
301		if v.id == fingers[1].id then
302			V = v
303			break
304		end
305	end
306	if not V then
307		stead.table.remove(fingers, 1) -- lost one?
308		return
309	end
310	v = V
311
312	local dx = v.x - fingers[1].x
313	local dy = v.y - fingers[1].y
314
315	local r = stead.math.sqrt(dx*dx + dy*dy)
316
317	if r < 8 then
318		return
319	end
320
321	if stead.math.abs(dx) >= stead.math.abs(dy) then
322		-- lr
323		if dx < 0 then
324			game:kbd(true, 'left')
325		else
326			game:kbd(true, 'right')
327		end
328	else
329		-- ud
330		if dy < 0 then
331			game:kbd(true, 'up')
332		else
333			game:kbd(true, 'down')
334		end
335	end
336end
337
338click_history = { {0,0}, {0,0}, {0,0}, {0,0} }
339cell_edit = function(x, y, a)
340	local c = cell_get(x, y)
341	if c == edit_c and not a then
342		edit_c = c + 1
343	else
344		edit_c = edit_c or c
345	end
346	if edit_c > 7 then
347		edit_c = 0
348	end
349	cell_set(x, y, edit_c)
350end
351
352game.click = function(s, x, y, a, b)
353	if edit_mode and not menu_mode then
354		local nx = math.floor(x / 32) * 2;
355		local ny = math.floor(y / 32) * 2;
356		if nx == player_x and ny == player_y then
357			cell_edit(player_x, player_y)
358		else
359			sprite_draw(player_x, player_y, cell_get(player_x, player_y));
360			player_x, player_y = nx, ny
361--			cell_edit(player_x, player_y);
362		end
363	end
364end
365
366game.kbd = function(s, down, key)
367	if key == 'space' or key == 'return' then
368		key_return = down
369		return
370	end
371
372	if key == 'escape' or key == 'backspace' then
373		if menu_mode ~= 'title' then
374			key_esc = down
375			return true
376		end
377		key_esc = false
378	end
379
380	if key == 'd' then
381		key_demo = down
382		return
383	end
384
385	if key == 'e' then
386		key_edit = down
387		return
388	end
389
390	if key >= '0' and key <= '7' then
391		key_num = down and key
392		return
393	end
394
395	if not down then
396		local k,v
397		local i
398		for k,v in ipairs(keys) do
399			if v == key then
400				i = k
401				break
402			end
403		end
404		if i then
405			stead.table.remove(keys, i)
406		end
407		return
408	end
409	s:kbd(false, key) -- release first
410	stead.table.insert(keys, 1, key)
411end
412
413
414banks = {
415}
416
417banks_init = function(s)
418	local k,v
419	local m = {}
420	for v in stead.readdir(instead_gamepath()) do
421		if v:find("%.map$") then
422			stead.table.insert(m, v)
423		end
424	end
425	stead.table.sort(m, function(a ,b)
426				if a == 'maps.map' then
427					return true
428				elseif b == 'maps.map' then
429					return false
430				else
431					return a < b
432				end
433	end)
434	for k, v in ipairs(m) do
435		local f = io.open(instead_gamepath().."/"..v)
436		if f then
437			local l
438			local name = v:gsub("%.map$", "")
439			local sname = name
440			local name_i18n = false
441			for l in f:lines() do
442				if not l:find("^%-%-") and not l:find("^[ \t]*$") then
443					break
444				end
445				if l:find("$Name:", 1, true) then
446					name = l:gsub("^.*%$Name:[ \t]*(.*)[ \t]*$", "%1")
447				elseif l:find("%$Name%([a-zA-Z]+%):") then
448					name_i18n = l:gsub("^.*%$Name%(([a-zA-Z]+)%):[ \t]*(.*)[ \t]*$", "%2")
449					name_lang = l:gsub("^.*%$Name%(([a-zA-Z]+)%):[ \t]*(.*)[ \t]*$", "%1")
450				end
451			end
452			if name_i18n then
453				stead.table.insert(banks, { title = name, title_i18n = { [name_lang] = name_i18n }, file = v, name = sname })
454			else
455				stead.table.insert(banks, { title = name, file = v, name = sname })
456			end
457			f:close();
458		end
459	end
460	for k,v in ipairs(banks) do
461		local title = v.title
462		if v.title_i18n and v.title_i18n[LANG] then
463			title = v.title_i18n[LANG]
464		end
465		v.spr = sprite.text(fn2, title, '#ff0000', 1)
466		v.sw, v.sh = sprite.size(v.spr)
467	end
468	nr_bank = 1
469end
470
471game.timer = function(s)
472	local rc
473	check_fingers()
474	if menu_mode then
475		rc = _G['menu_'..menu_mode..'_mode']()
476	end
477	if is_esc() then
478		title_enter()
479		return
480	end
481	if not rc then
482		game_loop()
483	end
484end
485
486pos2cell = function(x, y)
487	return stead.math.floor(x / 2), stead.math.floor(y / 2)
488end
489
490cell2pos = function(x, y)
491	return x * 2, y * 2
492end
493
494sprite_draw = function(x, y, c)
495	sprite.copy(sprites[c + 1], sprite.screen(), 16 * x, 16 * y)
496end
497
498cell_set = function(x, y, c)
499	x, y = pos2cell(x, y)
500	if x < 0 or x > 15 or y < 0 or y > 15 then
501		return
502	end
503	map[y + 1][x + 1] = c
504end
505
506cell_get = function(x, y, c)
507	x, y = pos2cell(x, y)
508	return map[y + 1][x + 1]
509end
510
511global {
512	player_x = 0;
513	player_y = 0;
514	player_movex = 0;
515	player_movey = 0;
516}
517
518is_key = function(n)
519	if keys[1] == n then
520		return true
521	end
522end
523
524is_return = function()
525	return key_return
526end
527
528is_esc = function()
529	return key_esc
530end
531
532is_anykey = function()
533	local c = key_any
534	key_any = false
535	if c then
536		key_return = false
537		key_esc = false
538	end
539	return c
540end
541
542input.key = stead.hook(input.key, function(f, s, down, key, ...)
543	if not key:find("escape") and not key:find("shift")
544		and not key:find("ctrl")
545		and not key:find("alt")
546		and not key:find("unknown") then
547		key_any = down
548	end
549	return f(s, down, key, ...)
550end)
551
552is_demo = function()
553	if key_demo then
554		return true
555	end
556end
557
558is_edit = function()
559	if key_edit then
560		key_edit = false
561		return true
562	end
563end
564
565human_stone = function(x, y)
566	local xx, yy, c
567	xx, yy = x + player_movex * 2, y + player_movey * 2
568	while true do
569		if xx >= 32 or yy >= 32 or xx < 0 or yy < 0 then
570			return human_stop(x, y)
571		end
572		c = cell_get(xx, yy)
573		if c == BEMPTY then
574			break
575		end
576		if c ~= BSTONE and c ~= BSTONE3 then
577			return human_stop(x, y)
578		end
579		xx, yy = xx + player_movex * 2, yy + player_movey * 2
580	end
581	cell_set(xx, yy, BSTONE_LAZY)
582	cell_set(x, y, BEMPTY);
583	sprite_draw(x, y, BEMPTY);
584	sprite_draw(xx, yy, BSTONE);
585	return human_move(x, y)
586--	local c = cell_get(xx, yy)
587--	if c == BEM
588end
589human_stop = function(x, y)
590	sprite_draw(player_x, player_y, BHUMAN)
591end
592human_gold = function(x, y)
593	-- sound
594	sound.play(sounds[SCLICK])
595	-- score
596	nr_score = nr_score + 1
597	return human_move(x, y)
598end
599
600human_move = function(x, y)
601	sprite_draw(player_x, player_y, BEMPTY)
602	if player_movex ~= 0 or player_movey ~= 0 then
603		if player_movex < 0 then
604			c = 16
605		elseif player_movex > 0 then
606			c = 18
607		elseif player_movey < 0 then
608			c = 12
609		else
610			c = 14
611		end
612		sprite_draw(player_x + player_movex, player_y + player_movey, c)
613		cell_set(x, y, c)
614		x, y = player_x + player_movex, player_y + player_movey
615	else
616		cell_set(player_x, player_y, BEMPTY)
617		cell_set(x, y, BHUMAN)
618	end
619	player_x, player_y = x, y
620end
621
622human_death = function(x, y)
623	explode(x, y)
624	level_stat().die = level_stat().die + 1
625	stead.autosave()
626	level_reset()
627end
628
629game_dispatch = function(c, x, y)
630	local dt = {
631		[BEMPTY] = human_move, -- 0
632		[BGRASS] = human_move, -- 1
633		[BSTONE] = human_stone, -- 2
634		[BGOLD] = human_gold, --3
635		[BBLOCK] = human_stop, --5
636		[BHEART] = human_death, --6
637		[BFLY] = human_death, --7
638		[BSTONE2] = human_stop, -- 8
639		[BSTONE3] = human_stone, -- stone
640		[BGOLD2] = human_stop, -- 8
641		[BGOLD3] = human_gold, -- gold
642		[20] = human_death,
643		[21] = human_death,
644	}
645	local fn = dt[c]
646	if not fn then
647		error("Unknown dispatcher: "..c)
648	end
649	return fn(x, y)
650end
651
652check_scatter = function(cc, x, y, d)
653	if d == 1 and x == 30 then
654		return false
655	end
656	if d == -1 and x == 0 then
657		return false
658	end
659	local c = cell_get(x + d * 2, y + 2)
660	if c ~= BEMPTY then
661		return false
662	end
663	c = cell_get(x + d * 2, y)
664	if c ~= BEMPTY then
665		return false
666	end
667	cell_set(x + d * 2, y, cc)
668	cell_set(x, y, BEMPTY)
669	sprite_draw(x, y, BEMPTY)
670	sprite_draw(x + d * 2, y, cc)
671	return true
672end
673fall1 = function(x, y, c)
674	local sc = true
675	if y < 30 then
676		local cd = cell_get(x, y + 2)
677		if cd == BEMPTY then
678			c = c * 2 + 4
679			cell_set(x, y + 2, c)
680			cell_set(x, y, c)
681			sprite_draw(x, y, BEMPTY);
682			sprite_draw(x, y + 1, c)
683			return x, y
684		else -- scatter
685			local dir = scatter_dir
686
687			scatter_dir = - scatter_dir
688			if not check_scatter(c, x, y, dir) then
689				dir = - dir
690				sc = check_scatter(c, x, y, dir)
691			end
692			if dir == 1 and sc then
693				return x + 2, y
694			end
695		end
696	end
697	if not sc then
698		cell_set(x, y, c)
699	end
700	return x, y
701end
702explode = function(x, y)
703	x = x + 2
704	local xe = x
705	y = y + 2
706	local ye = y
707	local xs = x - 4
708	if xs < 0 then
709		xs = xs + 2
710	end
711	local ys = y - 4
712	if ys < 0 then
713		ys = ys + 2
714	end
715	if xe > 31 then
716		xe = xe - 1
717	end
718	if ye > 31 then
719		ye = ye - 1
720	end
721	for y = ys, ye, 2 do
722		for x = xs, xe, 2 do
723			cell_set(x, y, BGOLD)
724			sprite_draw(x, y, BGOLD)
725		end
726	end
727	-- sound
728	sound.play(sounds[SDIE])
729--	local c = cell_get(player_x, player_y)
730--	if c ~= BHUMAN and c < 12 then
731--		level_stat().die = level_stat().die + 1
732--		prefs:store()
733--		stead.autosave()
734--		level_reset()
735--	end
736	return xe, ye
737end
738bank_stat = function()
739	local st = prefs.stat
740	local nam = banks[nr_bank].name
741	if nam ~= 'maps' then
742		if not prefs.maps_stat then
743			prefs.maps_stat = {}
744		end
745		st = prefs.maps_stat[nam]
746		if not st then
747			prefs.maps_stat[nam] = {}
748		end
749		st = prefs.maps_stat[nam]
750	end
751	return st
752end
753
754level_stat = function()
755	local st = bank_stat()
756	local lst = st[nr_level]
757	if not lst then
758		st[nr_level] = { }
759	end
760	st = st[nr_level]
761	if type(st.completed) ~= 'number' then
762		st.completed = 0
763	end
764	if type(st.die) ~= 'number' then
765		st.die = 0
766	end
767	if type(st.score) ~= 'number' then
768		st.score = 0
769	end
770	return st
771end
772
773fall = function()
774	local nr_gold = 0
775	local x, y, c
776	for y = 30,0,-2 do
777		x = 0
778		while x <= 30 do
779			if level_out then
780				return
781			end
782			c = cell_get(x, y)
783			if c == BGOLD or c == BGOLD3 or c == BGOLD2 then
784				nr_gold = nr_gold + 1
785			end
786			if c == BSTONE_LAZY then
787				c = BSTONE
788				cell_set(x, y, c)
789			elseif c == BGOLD or c == BSTONE then
790				x, y = fall1(x, y, c)
791			elseif c == BSTONE2 or c == BGOLD2 then
792				cell_set(x, y - 2, BEMPTY)
793				sprite_draw(x, y - 2, BEMPTY);
794				cell_set(x, y, c + 1)
795				sprite_draw(x, y, c + 1)
796			elseif c == BGOLD3 or c == BSTONE3 then
797				if y == 30 then
798					c = (c - 5) / 2
799					cell_set(x, y, c);
800					-- sound
801					sound.play(sounds[SFALL])
802					sprite_draw(x, y, c);
803				else
804					local cd = cell_get(x, y + 2)
805					if cd == BHUMAN or cd == BHEART or cd == BFLY or cd >= 12 then
806						-- explode
807						-- print("explode")
808						x = explode(x, y + 2);
809					else
810						if cd ~= BEMPTY then
811							-- sound
812							sound.play(sounds[SFALL])
813						end
814						c = (c - 5)  /2
815						x, y = fall1(x, y, c)
816					end
817				end
818			end
819			x = x + 2
820		end
821	end
822-- 	check if dead
823	c = cell_get(player_x, player_y)
824	if c ~= BHUMAN and c < 12 then
825--		print ("dec lives"..c)
826		level_stat().die = level_stat().die + 1
827--		prefs:store()
828		stead.autosave()
829		level_reset()
830		return
831	end
832	if nr_gold == 0 then -- or is_return() then -- hack
833		-- completed
834		if demo_mode then
835			level_reset()
836			return
837		end
838		if nr_level == nr_levels then
839			title_enter()
840			return
841		end
842		level_stat().completed = level_stat().completed + 1
843		if level_stat().score < nr_score then
844			level_stat().score = nr_score
845		end
846--		prefs:store()
847		stead.autosave()
848		local l = nr_level
849		nr_level = nr_level + 1
850		if nr_level == nr_levels then
851			-- lookup first undone
852			local i
853			for i=0,nr_levels - 1 do
854				if (not bank_stat()[i] or
855				    not bank_stat()[i].completed or
856				    bank_stat()[i].completed == 0) then
857					nr_level = i
858					break
859				end
860			end
861		end
862		if nr_level == nr_levels then
863			-- todo game over
864			nr_level = nr_levels
865			level_reset(l)
866			set_music 'snd/486.xm'
867		else
868			selected_level = nr_level
869			level_reset(l)
870		end
871	end
872end
873
874enemy_halflife = function(c)
875	if c < 16 then
876		c = c + 14
877	else
878		c = c - 14
879	end
880	return c
881end
882
883enemy_turn = function(x, y, c, w, e)
884	sprite_draw(x, y, BEMPTY)
885	c = enemy_halflife(c)
886	local dx, dy = w.dx, w.dy
887	e.x, e.y, e.dx, e.dy = x + dx, y + dy, dx, dy
888	sprite_draw(e.x, e.y, c)
889	cell_set(e.x + dx, e.y + dy, c)
890	return
891end
892
893enemy_logic = function(i)
894	local ways = {}
895	local x, y, dx, dy
896	local e = enemies[i]
897	x, y, dx, dy = e.x, e.y, e.dx, e.dy
898	local c = enemy_cell(i)
899	if (x % 2 ~= 0) or (y % 2) ~= 0 then -- odd
900		c = enemy_halflife(c)
901		cell_set(x - dx, y - dy, BEMPTY)
902		sprite_draw(x - dx, y - dy, BEMPTY)
903		x, y = x + dx, y + dy
904		cell_set(x, y, c)
905		sprite_draw(x, y, c)
906		e.x, e.y = x, y
907		return
908	end
909	 -- even, logic decision
910	local delta = -1
911	local half = true
912--	print "start"
913	while true do
914		local xm, ym
915		if half then
916			ym = y + delta * 2
917			xm = x
918		else
919			xm = x + delta * 2
920			ym = y
921		end
922		if xm >= 0 and ym >= 0 and xm < 32 and ym < 32 then
923			local cc = cell_get(xm, ym)
924			if (cc == BEMPTY or cc == BHUMAN) then
925			-- can walk
926				if half then
927					stead.table.insert(ways, { dx = 0, dy = delta })
928				else
929					stead.table.insert(ways, { dx = delta, dy = 0 })
930				end
931			end
932		end
933		if half then
934			half = false
935			-- continue
936		else -- flip
937			delta = - delta
938			if delta > 0 then -- another half
939				half = true
940				-- continue
941			else -- all scan is done
942				break
943			end
944		end
945	end
946--	print "break"
947	if #ways == 0 then
948		c = enemy_halflife(c)
949		cell_set(x, y, c)
950		sprite_draw(x, y, c)
951		return
952	end
953	if #ways > 1 then
954	-- do not walk backwards!
955		local i,k
956		for k,v in ipairs(ways) do
957			if v.dx == -e.dx and v.dy == - e.dy then
958				stead.table.remove(ways, k)
959				break
960			end
961		end
962	end
963
964	if #ways == 1 then -- only 1 path
965		enemy_turn(x, y, c, ways[1], e)
966		return
967	end
968	-- chose best one!
969	dx = player_x - x
970	dy = player_y - y
971	if dx < 0 then
972		dx = -1
973	else
974		dx = 1
975	end
976	if dy < 0 then
977		dy = -1
978	else
979		dy = 1
980	end
981	local best = 0
982	local best_w = ways[1]
983	for k,v in ipairs(ways) do
984		local new_best = dx + v.dx
985		local a = new_best
986		if new_best < 0 then new_best = - new_best end
987		local d = dy + v.dy
988		if d < 0 then d = - d end
989		new_best = new_best + d
990		if new_best > best then
991			best = new_best
992			best_w  = v
993		end
994	end
995	enemy_turn(x, y, c, best_w, e)
996end
997
998enemy_cell = function(i)
999	local e = enemies[i]
1000	local x, y, dx, dy = e.x, e.y, e.dx, e.dy
1001	if (x % 2) ~= 0 then
1002		x = x - dx
1003	end
1004	if (y % 2) ~= 0 then
1005		y = y - dy
1006	end
1007	return cell_get(x, y)
1008end
1009
1010enemy = function()
1011	if #enemies == 0 then
1012		return
1013	end
1014	local x, y, dx, dy, c, i, e
1015	i = 1
1016	while true do
1017		if i > #enemies then
1018			break
1019		end
1020		e = enemies[i]
1021		x, y, dx, dy = e.x, e.y, e.dx, e.dy
1022		c = enemy_cell(i)
1023		if c ~= BFLY and c ~= BHEART and c ~= 20 and c ~= 21 then
1024			if (x % 2 ~= 0) or (y % 2) ~= 0 then
1025				x = x + dx
1026				y = y + dy
1027				cell_set(x, y, BEMPTY)
1028				sprite_draw(x, y, BEMPTY)
1029			end
1030			-- remove enemy
1031			stead.table.remove(enemies, i)
1032		else
1033			enemy_logic(i)
1034			i = i + 1
1035		end
1036
1037	end
1038	c = cell_get(player_x, player_y)
1039	if c == BHUMAN then
1040		return
1041	end
1042	if c < 12 or c >= 20 then
1043		return human_death(player_x, player_y)
1044	end
1045end
1046
1047history_name = function(nr)
1048	local n = banks[nr_bank].name
1049	if n == 'maps' then
1050		return "demo"
1051	end
1052	return "demo-"..n.."-"
1053end
1054
1055history_check = function(nr)
1056	local p = instead_gamepath().."/"..history_name()..tostring(nr + 1)
1057	local f = io.open(p, "r")
1058	if not f then
1059		p = instead_savepath().."/"..history_name()..tostring(nr + 1)
1060		f = io.open(p, "r")
1061	end
1062	if not f then
1063		return false
1064	end
1065	f:close()
1066	return true
1067end
1068
1069history_load = function()
1070	local p = instead_gamepath().."/"..history_name()..tostring(nr_level + 1)
1071	local f = io.open(p, "r")
1072	if not f then
1073		p = instead_savepath().."/"..history_name()..tostring(nr_level + 1)
1074		f = io.open(p, "r")
1075	end
1076	if not f then
1077		history = {}
1078		return
1079	end
1080	local l
1081	history = {}
1082	for l in f:lines() do
1083		local v = {}
1084		for a in l:gmatch("[0-9-]+") do
1085			stead.table.insert(v, tonumber(a))
1086		end
1087		stead.table.insert(history, v)
1088	end
1089	f:close(p)
1090end
1091
1092history_store = function(n)
1093	local p = instead_savepath().."/"..history_name()..tostring(n + 1)
1094	local f = io.open(p, "w")
1095	local k,v
1096	for k,v in ipairs(history) do
1097		f:write(stead.string.format("%d %d %d\n", v[1], v[2], v[3]))
1098	end
1099	f:close(p)
1100end
1101
1102history_get = function()
1103	if #history == 0 then
1104		level_load()
1105		level_reset()
1106		return 0, 0
1107--		return 0, 0
1108	end
1109	local v = stead.table.remove(history, 1)
1110	v[3] = v[3] - 1
1111	if v[3] > 0 then
1112		stead.table.insert(history, 1, v)
1113	end
1114	return v[1], v[2]
1115end
1116
1117history_add = function(dx, dy)
1118	if #history == 0 then
1119		stead.table.insert(history, {dx, dy, 1})
1120		return
1121	end
1122	local n = #history
1123	local v = history[n]
1124	if v[1] == dx and v[2] == dy then
1125		v[3] = v[3] + 1
1126		return
1127	end
1128	stead.table.insert(history, {dx, dy, 1})
1129	return
1130end
1131
1132game_loop = function()
1133	if is_demo() and not demo_mode and history_check(nr_level) then
1134		level_load()
1135		level_reset()
1136		history_load()
1137		demo_mode = true
1138		return
1139	end
1140
1141	if demo_mode and is_anykey() then
1142		level_load()
1143		level_reset()
1144		return
1145	end
1146	if is_edit() then
1147		local new_mode = not edit_mode
1148		if edit_mode then
1149			level_store()
1150			bank_save()
1151		end
1152		level_load()
1153		level_reset()
1154		edit_mode = new_mode
1155		return
1156	end
1157	if edit_mode then
1158		edit_blink = not edit_blink
1159		local active_edit = false
1160		if is_key 'up' then
1161			active_edit = touch_max >= 1
1162			if acive_edit then cell_edit(player_x, player_y, active_edit); end
1163			sprite_draw(player_x, player_y, cell_get(player_x, player_y));
1164			player_y = player_y - 2
1165			edit_blink = false
1166		elseif is_key 'down' then
1167			active_edit = touch_max >= 1
1168			if acive_edit then cell_edit(player_x, player_y, active_edit); end
1169			sprite_draw(player_x, player_y, cell_get(player_x, player_y));
1170			player_y = player_y + 2
1171			edit_blink = false
1172		elseif is_key 'left' then
1173			active_edit = touch_max >= 1
1174			if acive_edit then cell_edit(player_x, player_y, active_edit); end
1175			sprite_draw(player_x, player_y, cell_get(player_x, player_y));
1176			player_x = player_x - 2
1177			edit_blink = false
1178		elseif is_key 'right' then
1179			active_edit = touch_max >= 1
1180			if acive_edit then cell_edit(player_x, player_y, active_edit); end
1181			sprite_draw(player_x, player_y, cell_get(player_x, player_y));
1182			player_x = player_x + 2
1183			edit_blink = false
1184		end
1185		if player_x < 0 then player_x = 0 end
1186		if player_x > 30 then player_x = 30 end
1187		if player_y < 0 then player_y = 0 end
1188		if player_y > 30 then player_y = 30 end
1189
1190		if is_return() or active_edit then
1191			edit_blink = true
1192			cell_edit(player_x, player_y, active_edit);
1193		elseif key_num then
1194			c = tonumber(key_num)
1195			cell_set(player_x, player_y, c)
1196		end
1197
1198		if not edit_blink then
1199			sprite.fill(sprite.screen(), player_x * 16, player_y * 16, 32, 32, "white");
1200		else
1201			sprite_draw(player_x, player_y, cell_get(player_x, player_y));
1202		end
1203		return
1204	end
1205	if nr_level == nr_levels and happy_end_spr_w then
1206		sprite.fill(sprite.screen(), (scr_w - happy_end_spr_w)/2, scr_h /2, happy_end_spr_w, scr_h / 2, "black")
1207	end
1208
1209	if (player_x % 2 == 0) and (player_y % 2 == 0)  then
1210		player_movex, player_movey = 0, 0
1211		if is_key 'up' then
1212			player_movey = -1
1213		elseif is_key 'down' then
1214			player_movey = 1
1215		elseif is_key 'left' then
1216			player_movex = -1
1217		elseif is_key 'right' then
1218			player_movex = 1
1219		end
1220		if demo_mode then
1221			player_movex, player_movey = history_get()
1222		else
1223			history_add(player_movex, player_movey)
1224		end
1225		if player_movex ~= 0 or player_movey ~= 0 then
1226			local x, y = player_x + player_movex * 2, player_y + player_movey * 2
1227			if x <= 31 and y<= 31 and x >= 0 and y >= 0 then
1228				local c = cell_get(x, y)
1229				game_dispatch(c, x, y)
1230			else
1231				human_stop(x, y)
1232			end;
1233		else
1234			human_stop(x, y)
1235		end
1236	else -- inertion
1237		local x, y
1238		sprite_draw(player_x, player_y, BEMPTY)
1239		x, y = player_x - player_movex, player_y - player_movey
1240		local c = cell_get(x, y)
1241		if c == BHUMAN then
1242			cell_set(x, y, BEMPTY)
1243		elseif c == BGOLD or c == BSTONE then -- fix of the original game
1244			sprite_draw(x, y, c)
1245		end
1246		x, y =  player_x + player_movex, player_y + player_movey
1247		c = cell_get(x, y)
1248		sprite_draw(x, y, c + 1)
1249		cell_set(x, y, BHUMAN)
1250		player_x, player_y = x, y
1251	end
1252	if menu_mode then
1253		return
1254	end
1255	fall();
1256	if menu_mode then
1257		return
1258	end
1259	enemy();
1260
1261	if nr_level == nr_levels then
1262		happy_end_render()
1263	end
1264
1265end
1266
1267orig_save = game.save
1268game.save = function(s, ...)
1269	if demo_mode then
1270		return
1271	end
1272	_G["_selected_level_"..banks[nr_bank].name] = selected_level -- old selection
1273	return orig_save(s, ...)
1274end
1275global { nr_bank = 1 };
1276
1277function bank_load()
1278	if nr_bank > #banks then
1279		nr_bank = 1
1280	end
1281	dofile (banks[nr_bank].file)
1282	nr_levels = #maps / 16
1283	print (nr_levels.." level(s) loaded...");
1284	local k,v
1285	for k,v in ipairs(happy_end_map) do
1286		stead.table.insert(maps, v)
1287	end
1288	if not _G["_selected_level_"..banks[nr_bank].name] then
1289		_G["_selected_level_"..banks[nr_bank].name] = 0
1290	end
1291	nr_level = _G["_selected_level_"..banks[nr_bank].name]
1292	if nr_level >= nr_levels then
1293		nr_level = 0
1294	end
1295	selected_level = nr_level -- new selection
1296end
1297
1298function bank_save()
1299	if nr_bank > #banks then
1300		return
1301	end
1302	local f = io.open(banks[nr_bank].file, "w")
1303	if not f then
1304		return
1305	end
1306	f:write(string.format("--$Name:%s\n", banks[nr_bank].title));
1307	local k,v
1308	if banks[nr_bank].title_i18n then
1309		for k, v in pairs(banks[nr_bank].title_i18n) do
1310			f:write(string.format("--$Name(%s):%s\n",
1311				k, v));
1312		end
1313	end
1314	f:write(string.format("maps = {\n"))
1315	for k = 1, nr_levels do
1316		f:write(string.format("-- %d\n", k - 1));
1317		local n
1318		for n=1,16 do
1319			f:write(string.format('"%s",\n', maps[(k - 1) * 16 + n]))
1320		end
1321	end
1322	f:write(string.format("};\n"))
1323	f:close()
1324end
1325
1326init = function()
1327	set_music_fading(500, 500)
1328	hook_keys('left', 'right', 'up', 'down', 'space', 'return', 'd', 'escape', 'e', '0', '1', '2', '3', '4', '5', '6', '7');
1329	load_sprites()
1330	load_sounds()
1331	offscreen = sprite.blank(scr_w, scr_h)
1332	banner = sprite.blank(scr_w, 32)
1333	sprite.fill(sprite.screen(), 'black');
1334	level_select = true
1335	banks_init();
1336end
1337
1338start = function()
1339	bank_load()
1340	if menu_mode ~= 'level_select' and menu_mode ~= 'title' and not demo_mode and menu_mode ~= 'bank_select' then
1341		level_load()
1342		level_movein()
1343	else
1344		title_enter()
1345	end
1346end
1347
1348dofile "i18n.lua"
1349dofile "menu.lua"
1350
1351main.nam = '!!!';
1352main.dsc = function(s)
1353	p (_("warning:Please, go to settings and switch on own themes feature!"))
1354end
1355