====== Модуль Proxymenu ======

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

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

proxymenu позволяет делать игры в стиле адвенчур для ZX-Spectrum. Примерами таких игр на INSTEAD являются: Зеркало, Kayleth, Резервная копия.

При использовании proxymenu, в отличие от классических игр INSTEAD, предполагается, что существуют различные варианты действий. Например: осмотреть, взять, бросить, говорить, отдать и др. При этом все взаимодействие с объектами происходит через область инвентаря.

Для создания элемента меню нужно воспользоваться одной из функций:
  * obj_menu - для создания действия, в котором участвует только один объект;
  * use_menu - для создания действия, в котором задействованы два объекта;
  * act_menu - для создания действия без объекта;

И добавить полученный элемент меню в игрока.

Рассмотрим все три функции.

**Действие без объекта:** act_menu(имя, название обработчика)

Например:

<code lua>
game.rest = function(s)
    p [[Я отдохнул.]]
end
rest = act_menu("ОТДОХНУТЬ", "rest")
place(rest, me())
</code>

ВНИМАНИЕ: Модуль proxymenu переопределяет функцию получения инвентаря inv(), поэтому для помещения предметов (а не пунктов меню!) следует использовать inv():add(), inv():del() и прочее, или put/remove/прочее(что, inv()), вместо put/remove/прочее(что, me()). Таким образом, для работы с объектами-меню используйте me(), а для игрового инвентаря: inv().

Как видим, при клике на пункт ОТДОХНУТЬ, вызовется обработчик game.rest, так как мы явно задали "rest" вторым параметром act_menu.

**Действие, в котором участвует один объект: **
obj_menu(имя, название обработчика, объекты сцены?, объекты инвентаря?, переходы?)

Например:
<code lua>
take_menu = act_menu("ВЗЯТЬ", "take", true);
place(take_menu, me())
</code>

Теперь, при щелчке на "ВЗЯТЬ", в выпадающем списке будут представлены объекты сцены, так как мы явно указали это через true (два последних параметра пусты -- что в данном случае синоним задания false).

При щелчке на объекте в списке "ВЗЯТЬ", будет вызвана следуюая цепочка обработчиков:

  * game.before_take -- если определена. При возврате false -- цепочка вызовов прерывается;
  * объект:take -- если определена. При возврате false -- цепочка вызовов прерывается;
  * game.after_take -- если определена. При возврате false -- цепочка вызовов прерывается;
  * game.take -- если определена, и все предыдущие обработчики вернули пустоту;

В качестве примера рассмотрим:
<code lua>
game.after_take = function(s, w)
    take(w)
end

apple = obj {
    nam = 'яблоко';
    dsc = [[На полу лежит яблоко]]; -- {} здесь не нужны, dsc опционален.
    take = "Я взял яблоко.";
}
</code>
Теперь, яблоко можно взять.

**Действие, в котором участвует два объекта: **

use_menu(имя, название обработчика, название обратного обработчика, название обработчика сам-на-себя, брать со сцены?, брать из инвентаря?, первый объект должен быть в инвентаре?)

Например:
<code lua>
use_menu = use_menu('ИСПОЛЬЗОВАТЬ', 'useon', 'used', 'useit', true, true);
place(use_menu, me())
</code>

Теперь, при щелчке на "ИСПОЛЬЗОВАТЬ", в выпадающем списке будут представлены объекты сцены и инвентаря, так как мы явно указали это через true,  (последний параметр пуст -- что в данном случае синоним задания false).

При щелчке на объекте в списке "ВЗЯТЬ", курсор перейдет в режим использования, в котором будет ожидаться второй клик. После клика на второй объект, вызовется следующая цепочка обработчиков (obj, obj2 -- объекты первого и второго клика):

  * game.before_useon -- если определена. При возврате false -- цепочка вызовов прерывается;
  * obj:useit() -- если obj == obj2
  * obj:useon(obj2) -- если obj не равен obj2 и если определена. При возврате false -- цепочка вызовов прерывается;
  * obj2:used(obj) -- если obj не равен obj2 и если прошлый обработчик пуст;
  * game.after_useon -- если определена. При возврате false -- цепочка вызовов прерывается;
  * game.useon -- если определена, и все предыдущие обработчики вернули пустоту;

Следет отметить, что название обратного обработчика, и название обработчика сам-на-себя -- не обязательные параметры.

===== Примеры использования =====

<code lua>
instead_version "1.6.3"
require "proxymenu"
require "hideinv"

game.forcedsc = true

minv = obj_menu('С СОБОЙ', 'exam', false, true);
mlook = obj_menu('ОСМОТРЕТЬ', 'exam', true);
mtake = obj_menu('ВЗЯТЬ', 'take', true);
mdrop = obj_menu('БРОСИТЬ', 'drop', false, true);
meat = obj_menu('ЕСТЬ', 'eat', true, true);
mpush = obj_menu('ТОЛКАТЬ', 'push', true);
muse = use_menu('ИСПОЛЬЗОВАТЬ', 'useon', 'used', 'useit', true, true);
mgive = use_menu('ОТДАТЬ', 'give', 'accept', false, true, true, true);
mwalk = obj_menu('ИДТИ', 'walk', false, false, true);

game.useit = 'Не помогло.'
game.use = 'Не сработает.'
game.give = 'Отдать? Ни за что!'
game.eat = 'Не буду это есть.'
game.drop = 'Еще пригодится.'
game.exam = 'Ничего необычного.'
game.take = 'Стоит ли это брать?'
game.push = 'Ничего не произошло.'

game.after_take = function(s, w)
	take(w)
end

game.after_drop = function(s, w)
	drop(w)
end

put(minv, me())
put(mlook, me())
put(mtake, me())
put(mdrop, me())
put(meat, me())
put(mpush, me())
put(muse, me())
put(mgive, me())
-- put(mwalk, me())

status = stat {
	_Turns = 0,
	life = function(s)
		s._Turns = s._Turns + 1;
	end;
	nam = function(s)
		return 'Статус игрока: '..s._Turns..'^';
	end
};
lifeon 'status'

put(status, me());

knife = obj {
	nam = 'ножик',
	dsc = 'На полу валяется ножик.',
	exam = 'Бесполезный перочинный ножик.',
}

main = room {
	nam = 'intro',
	hideinv = "true",
	dsc = 'Введение',
	exit = function(s)		
		inv():add('knife');
	end,
	obj = { vway('next','{Дальше}.', 'r1') }
}

cube = obj {
	nam = 'куб',
	dsc = 'В центре комнаты находится куб.',
	take = 'Вы взяли куб',
	exam = 'Мультифункциональный куб -- написано на кубе.',
	drop = 'Вы положили куб.',
	useit = 'Как можно использовать куб?',
	talk = 'Вы поговорили с кубом.',
	eat = function(s)
		return 'Вы не можете разгрызть куб.', false;
	end,
	open = 'Вы открыли куб.',
	close = 'Вы закрыли куб.',
	push = 'Вы толкаете куб.',
	give = function(s, w)
		return 'Вы пытаетесь отдать куб объекту: '..deref(w)..'.', false
	end,
	useon = function(s, w)
		return 'Вы пытаетесь юзать куб на объект: '..deref(w)..'. Получилось!'
	end,
	used = 'Куб поюзан.',
};

sphere = obj {
	nam = 'сфера',
	dsc = 'В центре комнаты находится сфера.',
	take = 'Вы взяли сферу',
	exam = 'Мультифункциональная сфера -- написано на сфере.',
	drop = 'Вы положили сферу.',
	useit = 'Как можно использовать сферу?',
	talk = 'Вы поговорили с сферой.',
	eat = function(s)
		return 'Вы не можете разгрызть сферу.', false;
	end,
	open = 'Вы открыли сферу.',
	close = 'Вы закрыли сферу.',
	push = 'Вы толкаете сферу.',
	give = function(s, w)
		return 'Вы пытаетесь отдать сферу объекту: '..nameof(w)..'.', false
	end,
	useon = function(s, w)
		return 'Вы пытаетесь юзать сферу на объект: '..nameof(w)..'. Получилось!'
	end,
	used = 'Сфера поюзана.',
};

r1 = room {
	nam = 'комната',
	dsc = 'Вы в комнате',
	obj = { cube, sphere },
}

</code>


