====== Модуль sprites ======

^ Подключение          | require "sprites"        |
^ Тип                  | игровой/расширение кода |
^ Зависимости          | theme                     |

===== Описание =====

Начиная с версии 1.4.0 INSTEAD поддерживает расширенные возможности для работы с изображениями, позволяющие в том числе делать 2d игры.

Модуль sprites предоставляет api, содержащие следующие функции:

^ Функция ^ Описание ^ 
| sprite.load(file_name) | загрузка спрайта из файла изображения. При этом функция вернет дескриптор загруженного спрайта (далее spr); |
| sprite.free(spr) | освобождение спрайта; |
| sprite.screen() | возвращает спрайт - игровой экран. Используется только в режиме прямого доступа; |
| sprite.font_scaled_size(size) | возвращает размер шрифта с учетом масштабирования шрифтов; |
| sprite.font(font_path, size) | загружает шрифт, возвращает дескриптор загруженного шрифта (далее font); |
| sprite.free_font(font) | выгружает шрифт; |
| sprite.font_height(font) | возвращает высоту шрифта в пикселях; |
| sprite.alpha(spr, alpha) | создает новый спрайт с заданной прозрачностью alpha (255 - не прозрачно); |
| sprite.dup(spr) | создает копию спрайта;|
| sprite.scale(spr, xs, ys) | масштабирование спрайта, для отражений используйте масштаб -1.0. (медленно! не для реального времени); |
| sprite.rotate(spr, angle) | поворот спрайта на заданный угол в градусах (медленно! не для реального времени); |
| sprite.text(font, text, col, [style]) | создание текстового спрайта, col - здесь и далее - цвет в текстовом формате (в формате '#rrggbb' или 'текстовое название цвета'); |
| sprite.size(spr) | возвращает ширину и высоту спрайта в пикселях; |
| sprite.text_size(font, text) | вычисляет размер, который будет занимать текстовый спрайт, без создания спрайта; |
| sprite.draw(src_spr, fx, fy, fw, fh, dst_spr, x, y, [alpha]) | рисование области src спрайта в область dst спрайта; (задание alpha сильно замедляет выполнение функции) |
| sprite.draw(src_spr, dst_spr, x, y, [alpha]) | рисование спрайта, укороченный вариант;(задание alpha сильно замедляет выполнение функции) |
| sprite.copy(src_spr, fx, fy, fw, fh, dst_spr, x, y, [alpha]) | копирование содержимого спрайта (рисование - замещение) |
| sprite.copy(src_spr, dst_spr, x, y, [alpha]) | копирование содержимого спрайта (рисование - замещение), укороченный вариант |
| sprite.fill(spr, x, y, [w, h, [col]]) | заполнение спрайта цветом; |
| sprite.pixel(spr, x, y, col, [alpha]) | заполнение пикселя спрайта; |
| sprite.pixel(spr, x, y) | взятие пикселя спрайта (возвращает цвет в текстовой форме); |
===== Примеры использования =====

Внимание!!! Состояние спрайтов не попадает в файл сохранения игры, поэтому задача восстановления игровой ситуации на основе сохраняемых переменных лежит на авторе игры. Общие рекомендации:

В функции init можно загружать и создавать те спрайты, которые будут необходимы во время цикла всей игры, например:
<code lua>
function init()
    bg = sprite.load 'background.png'    
    font = sprite.font ('sans.ttf', 32);
end
</code>

В функции start, вы можете восстанавливать игровую ситуацию на основе сохраненных переменных, так как start выполняется после загрузки игры или после первого запуска игры, например:
<code lua>
function start()
    if here() == main then
        main.pic = sprite.text(font, 'BIG ADVENTURE', 'black');
    end
end
</code>

Если вы создаете временные спрайты, освобождайте их, когда они больше не нужны, например:

<code lua>
function show_score()
    local t = sprite.text(font,'Score:'..tostring(score), 'white');
    sprite.draw(t, sprite.screen(), 0, 0);
    sprite.free(t);
end
</code>

Спрайты могут быть встроены в игру как и любая другая графика -- с помощью img/imgl/imgr или присвоены переменной pic сцены, но в последнем случае, любое изменение содержимое спрайта pic (например, в обработчике таймера) будет отражено в реальном времени в игре. Эту особенность можно использовать для анимационных квестов или заставок.

Например:
<code lua>
instead_version '1.3.5'
require 'sprites'
require 'timer'
main = room {
    nam = 'demo';
    pic = sprite.load 'box:320x200,black';
}

function init()
    timer:set(30);
end

game.timer = function()
    sprite.pixel(main.pic, rnd(320), rnd(200), 'white');
end
</code>

Начиная с 1.4.0 если обработчик не возвращает ничего, то игровая сцена не изменяется, за исключением модификаций pic сцены, если это спрайт.

Также, игра может задействовать режим прямого доступа, когда игра может рисовать непосредственно в экранную область INSTEAD. Переключение в режим осуществляется с помощью параметра темы:
<code lua>
scr.gfx.mode = direct
</code>
Вы можете задать этот параметр в theme.ini игры или менять его динамически, с помощью модуля theme.

В режиме прямого доступа, все отрисовки в специальный спрайт sprite.screen() отображаются в реальном времени.

Таким образом, если вы пишите 2d игру на INSTEAD типовой алгоритм ее работы выглядит следующим образом.

1. init() - загрузка спрайтов

2. start() - задание начальных значений или восстановление;

3. game.timer() - отрисовка кадра игры (модуль timer);

4. game.click() - получение событий мыши (модуль click);

5. game.kbd() - получение событий клавиатуры (модуль kbd);

INSTEAD всегда скрывает факт масштабирования от игры, поэтому, обычно игра работает независимо от выбранного разрешения. Все размеры и координаты выглядят так, как будто масштабирования нет. В отдельных случаях, в результате погрешностей округления это может стать проблемой (например подгонка tiles пиксель в пиксель). В этом случае автор может запретить масштабирование:
<code lua>
scr.gfx.scalable = 0
</code>
Начиная с версии 1.4.0 в INSTEAD существует возможность отслеживать интервалы времени в миллисекундах. Для этого используйте функцию  get_ticks().

Опрос или установка координат курсора мыши: mouse_pos([x, y]).

Пример работы со спрайтами:
<code lua>
instead_version "1.4.0"
require "timer"
require "sprites"
spr = sprite
function init()
	fnt = spr.font(theme.get 'win.fnt.name', 32);
	ball = spr.text(fnt, "INSTEAD 1.4.0", 'white', 1);
	ballw,ballh = spr.size(ball);
	bg = spr.load 'box:640x480,black';
	line = spr.load 'box:320x8,lightblue';
end

function start()
	timer:set(10)
	G = 9.81
	by = -ballh
	bv = 0
	bx = 320
	t1 = get_ticks()
end

function phys()
	local t = timer:get() / 1000;
	bv = bv + G * t;
	by = by + bv * t;
	if by > 400 then
		bv = - bv
	end
end

game.timer = function(s)
	local i
	for i = 1, 10 do
		phys()
	end
	if get_ticks() - t1 >= 20 then
		spr.copy(bg, spr.screen(), 0, 0);
		spr.draw(ball, spr.screen(), (640 - ballw) / 2, by - ballh/2);
		spr.draw(line, spr.screen(), 320/2, 400 + ballh / 2);
		t1 = get_ticks()
	end
end
</code>
Файл theme.ini
<code lua>
scr.w = 640
scr.h = 480
scr.gfx.mode = direct
</code>

Еще один вариант, пропускающий кадры при необходимости:
<code lua>
game.timer = function(s)
	local i
	for i = 1, 10 do
		phys()
	end
	if get_ticks() - t1 >= 15 then
		t1 = get_ticks()
		return
	end
	t1 = get_ticks()
	spr.copy(bg, spr.screen(), 0, 0);
	spr.draw(ball, spr.screen(), (640 - ballw) / 2, by - ballh/2);
	spr.draw(line, spr.screen(), 320/2, 400 + ballh / 2);
end
</code>