1local std = stead
2local type = std.type
3local instead = std.ref '@instead'
4
5-- luacheck: read globals instead_theme_var
6-- luacheck: read globals instead_theme_name
7-- luacheck: read globals instead_ticks
8-- luacheck: read globals instead_font_load
9-- luacheck: read globals instead_font_free
10-- luacheck: read globals instead_font_scaled_size
11-- luacheck: read globals instead_sprite_alpha
12-- luacheck: read globals instead_sprite_dup
13-- luacheck: read globals instead_sprite_scale
14-- luacheck: read globals instead_sprite_rotate
15-- luacheck: read globals instead_sprite_text
16-- luacheck: read globals instead_sprite_text_size
17-- luacheck: read globals instead_sprite_draw
18-- luacheck: read globals instead_sprite_copy
19-- luacheck: read globals instead_sprite_compose
20-- luacheck: read globals instead_sprite_fill
21-- luacheck: read globals instead_sprite_pixel
22-- luacheck: read globals instead_sprite_load
23-- luacheck: read globals instead_sprite_free
24-- luacheck: read globals instead_sprite_size
25-- luacheck: read globals instead_sprites_free
26-- luacheck: read globals instead_sprite_colorkey
27-- luacheck: read globals instead_sprite_pixels
28-- luacheck: read globals instead_mouse_pos
29-- luacheck: read globals instead_mouse_show
30-- luacheck: read globals instead_mouse_filter
31-- luacheck: read globals instead_finger_pos
32-- luacheck: read globals instead_noise1
33-- luacheck: read globals instead_noise2
34-- luacheck: read globals instead_noise3
35-- luacheck: read globals instead_noise4
36-- luacheck: read globals instead_render_callback
37-- luacheck: read globals instead_direct
38-- luacheck: read globals instead_busy
39-- luacheck: read globals instead_sprite_pixels
40-- luacheck: read globals instead_screen_size
41-- luacheck: read globals instead_screen_dpi
42
43-- theme
44instead.theme_var = instead_theme_var
45instead.theme_name = instead_theme_name
46instead.screen_size = instead_screen_size
47instead.screen_dpi = instead_screen_dpi
48
49local theme = std.obj {
50	nam = '@theme';
51	vars = {};
52	reset_vars = {};
53	{
54		win = { gfx = {}};
55		inv = { gfx = {}};
56		menu = { gfx = {}};
57		gfx = {};
58		snd = {};
59		scr = {};
60	};
61}
62
63function theme.restore(name)
64	if type(name) ~= 'string' then
65		std.err("Wrong parameter to theme.restore", 2)
66	end
67	local v = theme.vars[name]
68	if not v then
69		return
70	end
71	instead.theme_var(name, v);
72end
73
74function theme.set(name, val)
75	if type(name) ~= 'string' or val == nil then
76		std.err("Wrong parameter to theme.set", 2)
77	end
78	if not theme.reset_vars[name] then
79		theme.reset_vars[name] = instead.theme_var(name)
80	end
81	instead.theme_var(name, std.tostr(val));
82	theme.vars[name] = std.tostr(val);
83end
84
85function theme.reset(name)
86	if type(name) ~= 'string' then
87		std.err("Wrong parameter to theme.reset", 2)
88	end
89	local v = theme.reset_vars[name]
90	if not v then
91		return
92	end
93	instead.theme_var(name, v);
94	theme.vars[name] = nil
95	theme.reset_vars[name] = nil
96end
97
98function theme.name(...)
99	return instead.theme_name(...);
100end
101
102function theme.get(...)
103	return instead.theme_var(...);
104end
105
106function theme.scr.w()
107	return tonumber(theme.get 'scr.w')
108end
109
110function theme.scr.h()
111	return tonumber(theme.get 'scr.h')
112end
113
114function theme.win.reset()
115	std.for_all(theme.reset, "win.x", "win.y", "win.w", "win.h",
116		    "win.col.fg", "win.col.link", "win.col.alink",
117		    "win.fnt.name", "win.fnt.size", "win.fnt.height");
118end
119
120function theme.win.geom(x, y, w, h)
121	theme.set("win.x", x);
122	theme.set("win.y", y);
123	theme.set("win.w", w);
124	theme.set("win.h", h);
125end
126
127
128function theme.win.color(fg, link, alink)
129	theme.set("win.col.fg", fg);
130	theme.set("win.col.link", link);
131	theme.set("win.col.alink", alink);
132end
133
134function theme.win.font(name, size, height)
135	theme.set("win.fnt.name", name);
136	theme.set("win.fnt.size", size);
137	theme.set("win.fnt.height", height);
138end
139
140function theme.win.gfx.reset()
141	std.for_all(theme.reset, "win.gfx.up", "win.up.x", "win.up.y");
142	std.for_all(theme.reset, "win.gfx.down", "win.down.x", "win.down.y");
143end
144
145function theme.win.gfx.up(pic, x, y)
146	theme.set("win.gfx.up", pic);
147	theme.set("win.up.x", x);
148	theme.set("win.up.y", y);
149end
150
151function theme.win.gfx.down(pic, x, y)
152	theme.set("win.gfx.down", pic);
153	theme.set("win.down.x", x);
154	theme.set("win.down.y", y);
155end
156
157function theme.inv.reset()
158	std.for_all(theme.reset, "inv.x", "inv.y", "inv.w", "inv.h",
159		    "inv.col.fg", "inv.col.link", "inv.col.alink",
160		    "inv.fnt.name", "inv.fnt.size", "inv.fnt.height",
161		    "inv.mode");
162end
163
164function theme.inv.geom(x, y, w, h)
165	theme.set("inv.x", x);
166	theme.set("inv.y", y);
167	theme.set("inv.w", w);
168	theme.set("inv.h", h);
169end
170
171function theme.inv.color(fg, link, alink)
172	theme.set("inv.col.fg", fg);
173	theme.set("inv.col.link", link);
174	theme.set("inv.col.alink", alink);
175end
176
177function theme.inv.font(name, size, height)
178	theme.set("inv.fnt.name", name);
179	theme.set("inv.fnt.size", size);
180	theme.set("inv.fnt.height", height);
181end
182
183function theme.inv.mode(mode)
184	theme.set("inv.mode", mode);
185end
186
187function theme.inv.gfx.reset()
188	std.for_all(theme.reset, "inv.gfx.up", "inv.up.x", "inv.up.y");
189	std.for_all(theme.reset, "inv.gfx.down", "inv.down.x", "inv.down.y");
190end
191
192function theme.inv.gfx.up(pic, x, y)
193	theme.set("inv.gfx.up", pic);
194	theme.set("inv.up.x", x);
195	theme.set("inv.up.y", y);
196end
197
198function theme.inv.gfx.down(pic, x, y)
199	theme.set("inv.gfx.down", pic);
200	theme.set("inv.down.x", x);
201	theme.set("inv.down.y", y);
202end
203
204function theme.menu.reset()
205	std.for_all(theme.reset, "menu.bw",
206		    "menu.col.fg", "menu.col.bg", "menu.col.alpha",
207		    "menu.col.link", "menu.col.alink",
208		    "menu.fnt.name", "menu.fnt.size", "menu.fnt.height");
209end
210
211function theme.menu.bw(w)
212	theme.set("menu.bw", w)
213end
214
215function theme.menu.color(fg, bg, alpha, link, alink)
216	theme.set("menu.col.fg", fg);
217	theme.set("menu.col.bg", bg);
218	theme.set("menu.col.alpha", alpha);
219	theme.set("menu.col.link", link);
220	theme.set("menu.col.alink", alink);
221end
222
223function theme.menu.font(name, size, height)
224	theme.set("menu.fnt.name", name);
225	theme.set("menu.fnt.size", size);
226	theme.set("menu.fnt.height", height);
227end
228
229function theme.menu.gfx.reset()
230	std.for_all(theme.reset, "menu.gfx.button", "menu.button.x", "menu.button.y");
231end
232
233function theme.menu.gfx.button(b, x, y)
234	theme.set("menu.gfx.button", b);
235	theme.set("menu.button.x", x);
236	theme.set("menu.button.y", y);
237end;
238
239function theme.gfx.reset()
240	std.for_all(theme.reset, "scr.gfx.cursor.normal", "scr.gfx.cursor.use",
241		    "scr.gfx.cursor.x", "scr.gfx.cursor.y",
242		    "scr.gfx.mode", "scr.gfx.pad",
243		    "scr.gfx.bg");
244end
245
246function theme.gfx.cursor(norm, use, x, y)
247	theme.set("scr.gfx.cursor.normal", norm);
248	theme.set("scr.gfx.cursor.use", use);
249	theme.set("scr.gfx.cursor.x", x);
250	theme.set("scr.gfx.cursor.y", y);
251end
252
253function theme.gfx.mode(mode)
254	theme.set("scr.gfx.mode", mode);
255end
256
257function theme.gfx.pad(pad)
258	theme.set("scr.gfx.pad", pad);
259end
260
261function theme.gfx.bg(bg)
262	theme.set("scr.gfx.bg", bg);
263end
264
265function theme.snd.reset()
266	theme.reset("snd.click");
267end
268
269function theme.snd.click(w)
270	theme.set("snd.click", w);
271end
272
273-- sprites
274instead.ticks = instead_ticks
275instead.font_load = instead_font_load
276instead.font_free = instead_font_free
277instead.font_scaled_size = instead_font_scaled_size
278instead.sprite_alpha = instead_sprite_alpha
279instead.sprite_dup = instead_sprite_dup
280instead.sprite_scale = instead_sprite_scale
281instead.sprite_rotate = instead_sprite_rotate
282instead.sprite_text = instead_sprite_text
283instead.sprite_text_size = instead_sprite_text_size
284instead.sprite_draw = instead_sprite_draw
285instead.sprite_copy = instead_sprite_copy
286instead.sprite_compose = instead_sprite_compose
287instead.sprite_fill = instead_sprite_fill
288instead.sprite_pixel = instead_sprite_pixel
289instead.sprite_load = instead_sprite_load
290instead.sprite_free = instead_sprite_free
291instead.sprite_size = instead_sprite_size
292instead.sprites_free = instead_sprites_free
293instead.sprite_colorkey = instead_sprite_colorkey
294instead.sprite_pixels = instead_sprite_pixels
295
296instead.mouse_pos = instead_mouse_pos
297instead.mouse_show = instead_mouse_show
298instead.mouse_filter = instead_mouse_filter
299
300instead.finger_pos = instead_finger_pos
301
302instead.noise1 = instead_noise1
303instead.noise2 = instead_noise2
304instead.noise3 = instead_noise3
305instead.noise4 = instead_noise4
306
307instead.render_callback = instead_render_callback
308
309instead.direct = instead_direct
310
311std.busy = instead_busy
312
313local spr = {
314	__gc = function(s)
315		instead.sprite_free(s.spr)
316	end;
317	__tostring = function(s)
318		return s.spr
319	end;
320}
321
322spr.__index = spr
323
324local fnt = {
325	__gc = function(s)
326		instead.font_free(s.fnt)
327	end;
328	__tostring = function(s)
329		return s.fnt
330	end;
331}
332
333fnt.__index = fnt
334
335local spr_get = function(s)
336	if type(s) == 'string' then
337		return s
338	end
339	return std.tostr(s)
340end
341
342function fnt:new(nam)
343	if type(nam) ~= 'string' then
344		std.err("Wrong argument to fnt:new(): "..std.tostr(nam), 2)
345	end
346	local o = {
347		fnt = nam;
348		__save = function() end;
349	}
350	std.setmt(o, self)
351	return std.proxy(o)
352end
353
354function fnt:text(text, col, style, ...)
355	return spr:new(instead.sprite_text(self.fnt, text, col, style, ...))
356end
357
358function fnt:size(...)
359	return instead.sprite_text_size(self.fnt, ...);
360end
361
362function fnt:height(...)
363	local _, h = self:size(...)
364	return h
365end
366
367function spr:new(nam)
368	if type(nam) ~= 'string' then
369		std.err("Wrong argument to spr:new(): "..std.tostr(nam), 2)
370	end
371	local o = {
372		spr = nam;
373		__save = function() end;
374	}
375	std.setmt(o, self)
376	return std.proxy(o)
377end;
378
379function spr:alpha(alpha, ...)
380	return spr:new(instead.sprite_alpha(self.spr, alpha, ...));
381end
382
383function spr:colorkey(color, ...)
384	instead.sprite_colorkey(self.spr, color, ...);
385	return self
386end
387
388function spr:dup(...)
389	return spr:new(instead.sprite_dup(self.spr, ...));
390end
391
392function spr:scale(xs, ys, smooth, ...)
393	if smooth == nil then
394		smooth = true -- default is on
395	end
396	return spr:new(instead.sprite_scale(self.spr, xs, ys, smooth,...));
397end
398
399function spr:rotate(angle, smooth, ...)
400	if smooth == nil then
401		smooth = true -- default is on
402	end
403	return spr:new(instead.sprite_rotate(self.spr, angle, smooth, ...));
404end
405
406function spr:size()
407	return instead.sprite_size(self.spr);
408end
409
410function spr:draw(fx, fy, fw, fh, d, x, y, alpha)
411	if d == nil and x == nil and y == nil then
412		d, x, y, alpha = fx, fy, fw, fh
413		fx, fy, fw, fh = 0, 0, -1, -1
414	end
415	instead.sprite_draw(self.spr, fx, fy, fw, fh, spr_get(d), x, y, alpha);
416	return d
417end
418
419function spr:copy(fx, fy, fw, fh, d, x, y)
420	if d == nil and x == nil and y == nil then
421		d, x, y = fx, fy, fw
422		fx, fy, fw, fh = 0, 0, -1, -1
423	end
424	instead.sprite_copy(self.spr, fx, fy, fw, fh, spr_get(d), x, y);
425	return d
426end
427
428function spr:compose(fx, fy, fw, fh, d, x, y)
429	if d == nil and x == nil and y == nil then
430		d, x, y = fx, fy, fw
431		fx, fy, fw, fh = 0, 0, -1, -1
432	end
433	instead.sprite_compose(self.spr, fx, fy, fw, fh, spr_get(d), x, y);
434	return d
435end
436
437function spr:fill(x, y, w, h, col)
438	if h == nil and col == nil then
439		instead.sprite_fill(self.spr, 0, 0, -1, -1, x);
440		return self
441	end
442	instead.sprite_fill(self.spr, x, y, w, h, col);
443	return self
444end
445
446function spr:pixel(x, y, col, alpha)
447	if not col then
448		return instead.sprite_pixel(self.spr, x, y, col, alpha)
449	end
450	instead.sprite_pixel(self.spr, x, y, col, alpha)
451	return self
452end
453
454local screen = spr:new 'screen'
455
456local sprite = {
457	nam = '@sprite';
458}
459
460function sprite.new(w, h, ...)
461	if std.tonum(w) and std.tonum(h) then
462		local t = 'blank:'..std.tostr(std.math.floor(w))..'x'..std.tostr(std.math.floor(h))
463		return spr:new(instead.sprite_load(t))
464	end
465	local sp = instead.sprite_load(w, h, ...)
466	if not sp then
467		std.err("Can not load sprite: "..std.tostr(w), 2);
468	end
469	return spr:new(sp)
470end
471
472function sprite.fnt(name, sz, ...)
473	if not std.tonum(sz) then
474		std.err("No font size specified in sprite:fnt().", 2)
475	end
476	local fn = instead.font_load(name, sz, ...)
477	if not fn then
478		std.err("Can not load font: "..std.tostr(name), 2);
479	end
480	return fnt:new(fn)
481end
482
483function sprite.scr()
484	return screen
485end
486
487function sprite.direct(v)
488	return instead.direct(v)
489end
490
491function sprite.font_scaled_size(size)
492	return instead.font_scaled_size(size);
493end
494
495local render_cb = nil
496function sprite.render_callback(fn)
497	local old = render_cb
498	render_cb = fn
499	instead.render_callback(render_cb)
500	return old
501end
502
503std.obj(sprite)
504
505instead.sprite_pixels = instead_sprite_pixels
506
507local pfnt = {
508}
509
510pfnt.__index = pfnt
511std.setmt(pfnt, fnt)
512
513local pxl = {
514}
515
516pxl.__index = pxl
517
518function pxl:dup()
519	local w, h, s = self:size()
520	local p = instead.sprite_pixels(w, h, s)
521	if p then
522		self:copy(p)
523	end
524	return self:new(p)
525end
526
527function pxl:sprite(...)
528	return sprite.new(self, false, ...)
529end
530
531function pxl:tosprite(...)
532	return sprite.new(self, true, ...)
533end
534
535function pxl:draw_spr(fx, fy, fw, fh, d, x, y, alpha)
536	if d == nil and x == nil and y == nil then
537		instead.sprite_draw(self, 0, 0, -1, -1, spr_get(fx), fy, fw, fh);
538		return fx
539	end
540	instead.sprite_draw(self, fx, fy, fw, fh, spr_get(d), x, y, alpha);
541	return d
542end
543
544function pxl:copy_spr(fx, fy, fw, fh, d, x, y, alpha)
545	if d == nil and x == nil and y == nil then
546		instead.sprite_copy(self, 0, 0, -1, -1, spr_get(fx), fy, fw, fh);
547		return fx
548	end
549	instead.sprite_copy(self, fx, fy, fw, fh, spr_get(d), x, y, alpha);
550	return d
551end
552
553function pxl:compose_spr(fx, fy, fw, fh, d, x, y, alpha)
554	if d == nil and x == nil and y == nil then
555		instead.sprite_compose(self, 0, 0, -1, -1, spr_get(fx), fy, fw, fh);
556		return fx
557	end
558	instead.sprite_compose(self, fx, fy, fw, fh, spr_get(d), x, y, alpha);
559	return d
560end
561
562function pxl:scale(...)
563	return pxl:new(self:new_scaled(...))
564end
565
566function pxl:rotate(...)
567	return pxl:new(self:new_rotated(...))
568end
569
570local function poly(self, fn, t, ...)
571	if type(t) ~= 'table' then
572		std.err("Wrong argument to :poly()", 3)
573	end
574	if #t < 4 then
575		return
576	end
577	local n = #t
578	for i = 1, n, 2 do
579		if i == n - 1 then
580			fn(self, t[i], t[i+1], t[1], t[2], ...);
581		else
582			fn(self, t[i], t[i+1], t[i+2], t[i+3], ...);
583		end
584	end
585end
586
587function pxl:poly(t, ...)
588	poly(self, self.line, t, ...)
589end
590
591function pxl:polyAA(t, ...)
592	poly(self, self.lineAA, t, ...)
593end
594
595function pxl:new(p)
596	if type(p) ~= 'userdata' then
597		return
598	end
599	local t = getmetatable(p).__index
600	setmetatable(t, self)
601	return p
602end
603
604function pfnt:new(nam)
605	return fnt.new(self, nam)
606end
607
608function pfnt:text(text, col, style, ...)
609	local s = self
610	return pxl:new(instead.sprite_pixels(instead.sprite_text(s.fnt, text, col, style, ...)))
611end
612
613local pixels = {
614	nam = '@pixels';
615}
616
617function pixels.fnt(name, sz, ...)
618	if not std.tonum(sz) then
619		std.err("No font size specified.", 2)
620	end
621	return pfnt:new(instead.font_load(name, -sz, ...))
622end
623
624function pixels.new(...)
625	return pxl:new(instead.sprite_pixels(...))
626end
627
628std.obj (pixels)
629
630local rnd_seed = 1980 + 1978
631
632stead.mod_init(function()
633	rnd_seed = (std.os.time(stead.os.date("*t")) + rnd_seed + instead.ticks())
634	std.rnd_seed(rnd_seed)
635end)
636
637stead.mod_done(function()
638	sprite.render_callback() -- stop render
639--	instead.sprites_free();
640end)
641