Пустое описание для обработчика act

Иногда нужно сделать так, чтобы при реакции на act никакого текста в сцену не добавлялось. Если обработчик возвращает nil или false, то будет выведен game.act. Если обработчик возвращает пустую строку

''

, то будет выведен перевод строки.

Обработчик может вернуть true. В этом случае, результат не воспринимается как пустой, но вывод подавляется. Например:

trigger = obj {
    nam = 'триггер',
    _state = false,
    dsc = function(s)
        p 'На стене установлен {триггер}:'
        if s._state then
            p 'включено.';
        else
            p 'выключено.';
        end
    end,
    act = function(s)
        s._state = not s._state;
        return true
    end,
};
 
main = room {
    nam = 'комната',
    dsc = 'Сцена',
    obj = { 'trigger'},
};

Итератор объектов

Пример итератора по объектам:

    for k,v in opairs(objs()) do
        v = ref(v);
        -- здесь v -- это объект
    end

Замыкания

Lua позволяет создавать замыкания. Это можно применять для сокращения кода. Например можно сделать замыкание:

function _walk(where)
  return function()
    return walk(where);
  end
end

Тогда такой код:

mycar = obj {
        nam = 'моя машина',
        dsc = 'Перед хижиной стоит мой старенький {пикап} Toyota.',
        act = function(s)
                return walk('inmycar');
        end
};

При использовании _walk станет немного короче:

mycar = obj {
        nam = 'моя машина',
        dsc = 'Перед хижиной стоит мой старенький {пикап} Toyota.',
        act = _walk ('inmycar'),
};

Простые переходы

Пример использования техники, позволяющий делать простые переходы между локациями, как в книгах играх.

instead_version "1.6.0"
game.forcedsc = true
require "xact"
jump = xact('jump', code [[ walk(arg1) ]]);
 
-----
 
main = room {
    nam = 'Комната 1';
    dsc = [[ Тестовая игра!^^
           Перейти в {jump(room2)|комнату 2}. ]];
}
 
room2 = room {
    nam = 'Комната 2';
    dsc = function(s)
        p " Перейти в {jump(main)|комнату 1}. ";
        if visited(s) > 1 then
            p " Перейти в {jump(room3)|комнату 3}. ";
        end
    end
}
room3 = room {
    nam = 'Комната 3';
    dsc = "Конец игры!";
}

Обработчик, возвращающий последовательные описания

dsx = function(...)
	local a = { ... }
	return function(s)
		s._nx = s._nx or 0 
		s._nx = s._nx + 1
		if s._nx > #a then
			s._nx = #a
		end
		return a[s._nx]
	end
end

В игре.

main = room {
    nam  = 'Комната';
    dsc = dsx([[Первое описание]], [[Второе]], [[Последнее]]);
...
}

При показе описания комнаты каждый следующий осмотр покажет следующую строку в списке.

Динамический вывод текста

На основе модуля timer возможно реализовать реализовать привязанный ко времени вывод текста и объектов. Вот одна из возможных реализаций:

require "timer"
 
--далее идёт часть кода (тексты изменены)
primer = room {
  var {
      time = 1,
  };
 
  forcedsc = true,
 
  nam = 'В доме капитана',
 
  timer = function(s)
      s.time = s.time + 1;
      if s.time == 19 then
          objs():enable_all();
          return true
      end
      if s.time == 10 then -- redraw dsc
          return true;  
      end
  end;
  dsc = function(s)    
    pn 'Дверь открывает сам хозяин дома.';
    if s.time >= 10 then
      pn "Удивлены?";
    end
  end;
  obj = {
     vway('плата', '{"Немного".}^', 'par51'),
     vway('отказ', '{"Не очень"}', 'par228')
  };
  entered = function(s)
        timer:set(1000);
	objs():disable_all();
  end;
  left = code [[ timer:stop() ]];
};

В результате текст будет выводиться постепенно: вначале text1, на 10-й секунде text2, на 20-й будут показаны объекты.

Как сделать что-то при запуске/загрузке игры

function start() –your code here end

Проблемы с get_sound()

Эта функция вызывается движком INSTEAD для получения задачи на проигрывание звука, при этом, после того, как движок запустил звук, очередь заданий сразу очищается. Это означает, что get_sound нельзя использовать для отслеживания факта окончания проигрывания звукового файла.

Для корректного отслеживания звуков рекомендуется использовать средства модуля Sound.

Особенности xact

При подключении модуля xact становится возможным делать не только ссылки на объекты xact, но и на самые обычные, полноценные объекты obj.

require "xact"
 
wall = obj {
    nam = "стена",
    use = "Вы пытаетесь что-то сделать со стеной",
    act = "Вы дотронулись до стены",
}
 
shelf = obj {
     nam = полка,
     dsc = "На {wall|стене} висит книжная {полка}.",
}
 
main = room {
     nam = "Тестовая"
     obj {wall, room},
}

Все действия с такой ссылкой эквивалентны действиям с обычной ссылкой в dsc. На неё можно использовать другие объекты. Работают обработчики act, use, used, tak и тд.

Для того, чтобы ссылка была подсвечена, необходимо, чтобы объект, на который она ссылается, присутствовал в текущей сцене, т.e. было истинно seen(objectname).

Из этого правила есть одно исключение:

  • Ссылка на xact. На xact можно ссылаться независимо от того, находится ли он в текущей сцене или объявлен в глобальном пространстве.

Нельзя сделать ссылку, расположенную в сцене, которая ссылается на объект инвентаря.

Универсальная xact-ссылка

При необходимости вышеприведенные ограничения можно обойти. Посредством промежуточного xact можно создать ссылку, которая при клике по ней будет вызывать любой обработчик любого объекта, находяшегося где угодно. В следующем примере создаётся ссылка на объект в инвентаре, при щелчке на которую будет выведен его act:

link_act = xact( "link_act", function(_, o)
	o = stead.ref(o)
	p(o.act);
end);
 
phone = obj {
    nam = "Телефон",
    act = "Вы взяли телефон в руки.",
}
 
main = room {
    nam = ""
    enter = function(s)
        take (phone)
        p "{link_act(]]..stead.deref(phone)..[[)|Телефон}в вашем кармане издал странный звук."
    end, 
}

Реализация меню со списком опций (через xact)

instead_version "1.8.0"
require "xact";
 
 
main = room {
	nam = 'Настройки игры',
	_options = {					-- список опций. поскольку начинается с подчёркивания, то опции сохраняются вместе с игрой
	    subtitles = {nam = "Титры", state = false},
	    illustrations = {nam = "Иллюстрации", state = true},
	    sound = {nam = "Звук", state = true},
	    etc = {nam = "3D модели :) ", state = false},
	},
 
	dsc = function(s)
	    local i; 
	    for i, _ in pairs(s._options) do		
		local c = s._options[i];
		local t;
		if c.state then t = "Включено" else t = "Отключено" end;
		pn (c.nam, ": ", "{option_xact(", i, ")|", t, "}" );		
	    end;
	end,
 
	obj = {xact("option_xact", function(_,i)			-- действие по клику на пункт меню
		main._options[i].state = not main._options[i].state;
		stead.need_scene(); return true					-- обновляем экран
	    end)
    }
}

Реализация меню со списком опций (без xact)

instead_version '1.9.1'
 
main = room {
	var {
		options = {
			titles = { nam = "Титры", state = true },
			art = { nam = "Иллюстрации", state = true },
		},
	},
	switch = function (nam)
		local i
		for i in pairs (main.options) do
			if main.options[i].nam == nam then
				main.options[i].state = not main.options[i].state
			end
		end
	end,
	nam = 'Настройки игры',
	dsc = nil,
	enter = function (s)
		local i
		for i in pairs (s.options) do
			local c = 'if main.options.' .. i .. '.state then v="включены" else v="выключены" end p("' .. s.options[i].nam .. ': {" .. v .. "}^")'
			put (vobj (s.options[i].nam, code (c)))
		end
	end,
	act = function (s, w)
		s.switch (w)
		return true
	end,
}

Не забудьте заменить во всех вызовах main на имя сцены, в которой будет применяться данный код.

Динамические объекты и клонирование

Обычные статические объекты заранее объявлены в коде. Все они существуют в единственном числе. Если несколько раз добавить такой объект к примеру в в инвентарь, то там образуется несколько ссылок на один и тот-же объект и возникнет иллюзия что объектов несколько. На самом деле все эти ссылки ведут на один единственный объект.

Иногда необходимо создавать объекты в самой игре на лету. Например, таким образом мы можем генерировать неограниченное кол-во клонов статического объекта. Нижеприведенный пример показывает как корректно клонировать объекты типа obj, но аналогичную методику можно использовать и с room, создавая новые сцены прямо во время игры на лету.

instead_version "1.7.0"
 
-- Пример клонирования obj. (c) z-Hunter, 2012
showdsc = function (s)
	local i, o;
	p (s.cdsc);
	for i, o in opairs(objs(s)) do	
		p(o.cdsc);
	end;
end;
 
 
apple = obj {
    nam = "яблоко",
    inv = function (s) p ("На яблоке написан номер: ", s._id); end,
    _id = "0 (это оригинал)";
    tak = "Вы взяли яблоко";
    cdsc = "Тут лежит яблоко";
    -- dsc = function(s) p("На столе лежит {"..s.nam.."}. "); end;
};
 
function table.deepcopy(t)	
	local k; local v;
	if type(t) ~= "table" then return t end;
	local mt = getmetatable(t);
	local res = {};
	for k,v in pairs(t) do
		if type(v) == "table" then
			v = table.deepcopy(v)
		end;
		res[k] = v;
	end;
	setmetatable(res,mt);
	return res;
end;
 
function clone(s)
    local ret = table.deepcopy(s);
    local r = tostring(rnd(1000));	
    ret.nam = "яблоко"..r;
    ret._id = r;
    return ret;
end
 
button = obj {
	nam = "кнопка1",
	dsc = "{'Добавить яблоко'} ",
	act = function (s)
		p "Вы нажали на кнопку и что-то произошло.";
		local a = apple;
		put (new ([[clone(]]..stead.deref(a)..[[)]]), me());
	end,
 
};
 
button2 = obj {
	nam = "кнопка2",
	dsc = "и {'Удалить яблоко'}.",
	act = function (s)
		local i = nil;
		p "Вы нажали на кнопку и что-то произошло.";
 
		for k, v in opairs (pl.obj) do		-- ищем 1 любое яблоко
			if string.find (v.nam, "яблоко") then
				remove (v, pl);
				break;
			end;
		end;	
	end,
 
}
 
table_ = obj {
	nam = "стол",
	dsc = function(s) showdsc(s) end,
	-- dsc = "В центре комнаты стоит {стол} с двумя кнопками: ",
	act = "Сюда можно выкладывать яблоки.",
	obj = {"button", "button2", "apple"},
	used = function (s, w)
		drop (w);
		p "Вы выложили яблоко.";
	end;
}
 
main = room {
	nam = "Тестовая",
	dsc = "Это комната для психологических тестов.",
	obj = {"table_"},
 
}

Важно, чтобы динамические объекты создавались через функцию-конструктор, в данном случае clone(s). Иначе они не будут сохраняться и исчезнут при загрузке игры.

Добавление переменных в var на лету

В движке есть полезная функция: stead.add_var (куда, { как будто то это var } )

Например, вот так можно добавить переменную myvar в объект game:

stead.add_var(game, {myvar = true})

Эта переменная будет попадать в файл сохранения как и всё, что находится в секции var.

Пример реализации фонарика

Данный пример показывает, как реализовать фонарик и комнаты без света таким образом, что при отсутствии фонаря/выключенном фонаре комнаты по-прежнему доступны, работают переходы между ними, однако для всех комнат отображается стандартное описание по типу «мало света», и объекты в этих комнатах либо неактивны, либо вообще невидимы.

Фонарик в примере ниже реализован через объект flash, который включается и выключается через метод inv (т.е. двойным щелчком по фонарику в инвентаре).

function tcall(f,s)
  if f == nil then
    return f;
  elseif type(f) == "function" then
    return tcall(f(s),s);
  else
    return f;
  end
end
 
flash = obj {
   _on          = false
  ,nam          = function(s)
                    return "Фонарь ("..s:statefun()..")";
                  end
  ,statefun     = function(s)
                    if s._on then
                      return "включён";
                    else
                      return "выключен";
                    end
                  end
  ,inv          = function(s)
                    s._on = not s._on;
                    return "Фонарь "..s:statefun()..".";
                  end
}
 
function flash_on()
  return have(flash) and flash._on;
end
 
function darkroom(tab)
  local dsc = tab.dsc;
  tab.dsc = function(s)
    if flash_on() then
      return tcall(dsc,s);
    else
      return "В комнате темно. Я ничего не вижу, кроме смутных очертаний стен.";
    end
  end
  return room(tab);
end
 
function darkobj(tab)
  local dsc = tab.dsc;
  tab.dsc = function(s)
    if flash_on() then
      return tcall(dsc,s);
    else
      return tcall(s.darkdsc,s);
    end
  end
  return obj(tab);
end

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

scene1 = darkroom {
   nam          = "Подвал"
  ,obj          = { "scene1_box" }
  ,dsc          = "Слабый луч фонаря едва освещает грязные стены."
}
 
scene1_box = darkobj {
   nam          = "Коробка"
  ,dsc          = "На полу лежит лежит фанерная {коробка}."
  ,act          = "Коробка пуста."
}

Т.е. вы пишите свой код как обычно - так, как будто свет всегда есть. При этом вы просто используете конструкторы darkobj и darkroom вместо стандартных. Темнота обрабатывается автоматически. Если темно, то все объекты, созданные через darkobj, становятся невидимы, а у комнаты отображается стандартное «темное» описание. Есть также возможность использовать отдельное описание объектов для случая темноты:

scene1_box = darkobj {
   nam          = "Коробка"
  ,dsc          = "На полу лежит лежит фанерная {коробка}."
  ,darkdsc      = "Под моими ногами что-то лежит, но я никак не могу разобрать что."
  ,act          = "Коробка пуста."
}

В таком случае, когда темно, будет использоваться описание из атрибута darkdsc.

Включение режима use

Как известно, если кликнуть в инвентаре объект (obj), INSTEAD перейдёт в режим «use», то есть поменяет курсор, и даст возможность щёлкнуть на какой-то другой объект. С помощью xact можно из любого места кода перевести INSTEAD в этот режим для любого объекта в данной сцене.

-- context menu and "use" mode triggering example from ConText Framework. (c) z-Hunter, 2015
instead_version "1.7.0"; require "xact";
 
game.use = "Не сработает...";	
 
cxact = xact( "cxact",  function(_, f, o)	-- xact-обработчик гиперссылок. вызывается когда юзер нажал на ссылку
											-- первым параметром instead передает ссылку на вызвавший объект, которая нам не нужна (_)
											-- второй параметр (f) - имя пункта меню 
        o = stead.ref(o)					-- третий параметр (o) - превращенная в текст ссылка на объект, которую превращаем обратно
        ClearUsemode(true);						-- сбрасываем режим use
		o.scene_use = true; o.menu_type = true;
		return o.Choices[f](o)				-- запускаем функцию, прикрепленную к соотв. пункту меню
end)
 
function ClearUsemode(f)				-- 	сбрасывет режим use всех объектов в сцене/инвентаре и выставляет menu_type =f	
		local i; local s = here();
		for _, i in opairs(objs(s)) do			
			i.scene_use = false; i.menu_type = f;
		end;
		for _, i in opairs(inv()) do			
			i.scene_use = true; i.menu_type = f;
		end;
end;
 
function cmenu(s)							-- выводим контекстное меню объекта (s)	
	ClearUsemode(false);
	s.scene_use = true; s.menu_type = false;  -- устанавливаем режим use
    p(s.nam,": ");								-- выводим название объекта
	local txt, fun
    for txt, fun in pairs(s.Choices) do			-- перебор всех пунктов меню
        p("^> {cxact(".. txt.. ",".. stead.deref(s).. "|".. txt.. "}");    -- для каждого генерируем гиперссылку
    end;
    pn();
 
	p("> {"..stead.deref(s).."|Использовать с...}" );     -- и наконец гиперссылку на сам объект для переключения режима use	
end;
 
---- main part
 
book = obj {
    nam = "Книга",
    dsc = "На полу лежит {книжка}",
    act = function(s) cmenu(s) end,
	inv = function(s) cmenu(s) end,
    Choices = {
        ["Взять"] = function(s)
			p "Вы взяли книжку"; take(s);
		end,
        ["Читать"] = function(s)
			p "Книга называется: <<Еще раз об особенностях использования xact в INSTEAD>>. Читать дальще не хочется. ";
		end,
        ["Осмотреть"] = function(s) p "Книга тяжелая."; end
    },
};
 
 
shelf = obj {
	nam = "Полка",
	dsc = "На стене висит книжная {полка}. ",
	act = function(s) cmenu(s) end,
	_f = false,
	Choices = {        
        ["Осмотреть"] = function(s)
			if not s._f then p "В ряду книг, заполняющих полку, заметно свободное место. ";
			else p "Вы проводите пальцем по корешкам книг и улыбаетесь. Теперь все на местах.";
			end;
		end,
    },
	used = function(s,w)
				if w == book then
					p"Вы поставили книгу на полку.";
					s._f = true; remove(book, me());
				end;	
	end,
 
};
 
main = room {
    nam = "Тест",     
	dsc = "Это, так сказать, комната";
	obj = {shelf, book };
};

Перебор файлов в каталоге игры

Следующий кусок кода может пригодиться, если вы хотите получить список всех файлов в подкаталоге.

Он заполняет переменную s.playlist музыкой из папки mus, но вы также можете, например, автоматически подключать все файлы с исходным кодом через dofile.

local f;
for f in stead.readdir(get_gamepath().."/mus") do
    if f ~= '.' and f ~= '..' then
        table.insert(s.playlist, f)
    end
end
Навигация
Печать, экспорт
Инструменты
Язык
Перевод этой страницы:
Инструменты
Ссылки