Topic: Проблемы STEAD API

Хотелось бы спросить про одну проблему: одинаковое имя для конструктора объекта и для одного из его свойств, а именно "obj".

Т.е. становятся возможными конструкции типа:

main = room {
  nam = "Комната",
  obj = {
    obj {
      nam = "кирпич",
      dsc = "обычный красный кирпич",
    },
  },
}

Если я правильно понял, то Lua в данном случае различает объекты только потому, что в момент вызова конструктора obj {...} свойство main.obj еще не определено.
Но что будет если сделать так:

main = room {
  nam = "Комната",
  obj = {
    obj {
      nam = "кирпич",
      dsc = "обычный красный кирпич",
    },
    enter = function(s,f)
      local backupobj = obj;
    end,
  },
}

Что в этом случае будет содержаться в backupobj - ссылка на таблицу main.obj или на глобальную функцию-конструктор obj? Из мануала по Lua мне этого понять не удалось.
Конечно, можно явно написать local backupobj = s.obj, но что делать если я хотел именно забекапить конструктор? Еще можно просто проверить как будет работать код, но имхо - это плохой подход. Нужно правило, по которому можно без проверки сказать, как будет вести себя тот или иной код.

Если такое правило есть - пожалуйста покажите мне его.
Если правила нет - нужно придумать как с этим дальше жить smile Ломать обратную совместимость конечно не хочется, но выход из ситуации должен быть.

Re: Проблемы STEAD API

Odyssey wrote:

Хотелось бы спросить про одну проблему: одинаковое имя для конструктора объекта и для одного из его свойств, а именно "obj".

Что в этом случае будет содержаться в backupobj - ссылка на таблицу main.obj или на глобальную функцию-конструктор obj? Из мануала по Lua мне этого понять не удалось.

Откровенно говоря, я никогда не думал что lua будет экранировать имена в данном случае, грубо говоря:

a = {
      z = 2,
      b = z, --
}

b = z всегда понимается как глобальная z, никакого контекста здесь нет - -это просто таблица. Скорее надо искать не такое правило, а обратное, почему тут контекст появляется? smile

Re: Проблемы STEAD API

Я ошибся в своем примере. В нем enter объявляется у объекта, а должен у комнаты:

main = room {
  nam = "Комната",
  obj = {
    obj {
      nam = "кирпич",
      dsc = "обычный красный кирпич",
    },
  },
  enter = function(s,f)
    local backupobj = obj;
  end,  
}

Вот это будет правильный случай непонятного поведения, хотя и предыдущий был не очень понятен.

Peter wrote:
a = {
    z = 2,
    b = z, --
}

Тут тоже странно, хотя еще как-то можно понять. Инициализация z происходит прежде чем закончилась инициализация a, поэтому контектста у z в данный момент нет, и каждый элемент a инициализируется сам по себе. А в примере с backupobj enter - функция, которая будет выполняться когда вся инициализация уже закончена..

Похоже ответ нужно искать на ресурсах посвященных Lua..

Re: Проблемы STEAD API

Odyssey wrote:

Тут тоже странно, хотя еще как-то можно понять. Инициализация z происходит прежде чем закончилась инициализация a, поэтому контектста у z в данный момент нет

А я все пытаюсь сказать, что нет никакого контекста у таблиц. smile Если бы были -- было бы удобнее, кстати. Но их нет. Если окажется, что есть -- я удивлюсь.

Re: Проблемы STEAD API

Peter wrote:

b = z всегда понимается как глобальная z, никакого контекста здесь нет

Точно, ты прав. На форуме lua.ru подтвердили.

поле - это не отдельная переменная, а часть таблицы! Обращаться к полу надо непосредственно через таблицу.

И после этого я нашел таки соответствующую строчку документации:

The language supports ... by providing a.name as syntactic sugar for a["name"].

Т.е. main.obj и a.z - это не более чем main["obj"] никак не относящееся к obj и a["z"], никак не относящееся к z.

Вопрос снят.

Re: Проблемы STEAD API

Odyssey wrote:

Т.е. main.obj и a.z - это не более чем main["obj"] никак не относящееся к obj и a["z"], никак не относящееся к z.

Кстати, да. smile Интересное все-таки ООП у lua.

Re: Проблемы STEAD API

Луа вообще, по-моему один из самых красивых языков программирования.

Re: Проблемы STEAD API

Обнаружил следующие ошибки в stead.lua

1.seen()  не ищет вложенные объекты и работает с disabled объектами
2.have() работает с disabled объектами. Не ищет вложенные объекты (by design, позволяет переопределять inv()).

Проблема в том, что причина -- в методе srch списка. Иногда он работает с disabled объектами, если поиск осуществляется по самому объекту (не по имени).

smile

Re: Проблемы STEAD API

smile Ожидается 0.9.3? Или нужно решение проблемы с srch?

Re: Проблемы STEAD API

Odyssey wrote:

smile Ожидается 0.9.3? Или нужно решение проблемы с srch?

Просто думал сначала, что совместимость полетит. Теперь уже уверен, что нужно исправить. smile Исправил в svn.

11 (edited by Odyssey 2009-11-14 02:03:00)

Re: Проблемы STEAD API

Есть не совсем предрелизный вопрос: а не должен ли drop/dropf предвариательно проверять, есть ли предмет в инвентаре? А то получается, что можно сделать так:
drop('cellphone');
drop('cellphone');
и на сцене будут лежать два мобильника.

Поскольку фишка drop по сравнению c put -- это обслуживание инвентаря, я предлагаю сделать обслуживание полным, и в начало drop добавить проверку на наличие объекта. Т.е. если объекта в инвентаре нет -- то ничего не делаем, что-то типа

if not have(obj) then
  return;
end

В коте и истории drop используется только в use/used обработчиках, что подразумевает наличие объекта. И по крайней мере эти два квеста сломаться от данного изменения не должны.

Для себя я могу переопредлить drop сам, поэтому вопрос скорее принципиальный.

P.S.
То же самое относится к take. Разве мы можем взять что-то со сцены, если этого "чего-то" там нет?

Я не настаиваю на обязательной смене поведения, просто хочу понять, это фишка или недосмотр.

P.P.S
Пока добавил для себя:

function trydrop(obj)
  if have(obj) then
    drop(obj);
    return true;
  else
    return false;
  end;
end;

function trytake(obj, wh)
  if seen(obj, wh) then
    take(obj);
    return true
  else
    return false;
  end;
end;

Re: Проблемы STEAD API

Что касается take -- то я вовсю его использую как замена put(w, me()).
То есть просто добавить в инвентарь..   drop -- положить И убрать из инвентаря.
Нет в инвентаре -- только положу. smile

Имхо эти функции планировались как все-таки достаточно низкоуровневые (там практически везде нет никаких проверок) и все равно мы там где нужно проверки ставим сами или же они естественным образом возникают как часть алгоритма. В общем -- я за старое поведение, так как опыт написания говорит за большую гибкость. В любом случае -- пока не вижу веских доводов для такой жесткой смены совместимости. Максимум -- написать другой слой: Take, Drop итд -- то, что мы и делали в Зеркале.

Но смущает что на сцене будет  2 мобильника -- ты проверял? В obj не может быть 2 одинаковых объекта. Одного и того-же объекта. По идее получится положить только один.
Если нужно много однотипных -- используй new.

Re: Проблемы STEAD API

Peter wrote:

Но смущает что на сцене будет  2 мобильника -- ты проверял?

Нет, не проверил sad Тогда всё понятно, два раза одну и ту же вещь я не выброшу, поскольку вторая не поместится на сцене.
Значит пока сделаю свой слой, благо в Lua это просто и приятно smile

Re: Проблемы STEAD API

Хотелось бы зафиксировать здесь ещё одно пожелание по stead. Сам неизвестно когда соберусь это сделать, а так хоть будет напоминание о проблеме, и заодно узнаю мнение Peter'а.

Откуда возникла проблема:
Мне вдруг захотелось в диалогах после щечка на фразе выводить не

описание сцены

ответ курсивом

а

-- выбранную фразу диалога
-- ответ на эту фразу

Записывать всё это в описание сцены -- не очень хороший способ, и я решил заглянуть в stead и посмотреть чем он может мне помочь. И либо я невнимательно смотрел, либо механизм вывода сцены очень замороченно адаптировать под другой способ вывода.
Конкретно, мне не нравятся две вещи в функции stead cmd:
1) результат формируется параллельно со сбором данных
2) в результат захардкодено форматирование (txtem).
Т.е. если пользователю stead нужно выводить элементы сцены не так, как задумано разработчиком, ему нужно переопределять всю функцию cmd.

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

Таким образом:
* стало бы возможным создание пользовательских функций вывода
* код движка стал бы понятнее.

Поскольку я сам из-за недостатка времени пока смирился с текущей ситуацией, в обозримом будущем делать это изменение я не планирую, и разумеется никого другого не прошу. Но интересует принципиальное отношение к такому изменению. Может быть кто-нибудь будет обоснованно против, и тогда я буду терпеть текущую ситуацию уже с совершенно спокойной совестью smile

Re: Проблемы STEAD API

Я начал попытку упрощения cmd, см. svn. Есть идеи, куда двигаться дальше?

16 (edited by Odyssey 2010-01-15 02:01:00)

Re: Проблемы STEAD API

Хм, здорово, пока вроде бы всё. Либо я сегодня уже плохо соображаю. Пока приходит в голову только мелочёвка:

1. Хотелось бы комментариев к

if v == false then
    return fmt(r), false;
end

Плюс может быть удастся сделать одну точку выхода из cmd? Ну или две, одна из которых возвращает nil.. Как думаешь, нужно ли это? Вобще-то мелочь, просто когда мне нужно было подампить команды, пришлось заворачивать cmd в доп. функцию.

2. Я бы отодвинул объявление vv в cmd куда-нибудь поближе к месту её использования.

3. Хотелось бы сброса ACTION_TEXT в nil если он не строковый, и его заморозки для документирования )

Может быть на свежую голову найду что-то ещё. И теперь осталось оттестить изменения.

P.S.
Жаль, что полноценное автоматизированное тестирование для текстовых квестов сложно, очень много ветвлений. Хотя надо будет всё-таки подумать о протоколировании ручного прохождения (это не сложно сделать даже на чистом lua), последующем воспроизведении тех же команд и сравнении результата. Иначе регрессии очень трудно будет ловить..

17 (edited by Odyssey 2010-01-17 11:50:35)

Re: Проблемы STEAD API

Peter, есть ещё одна проблема, правда с cmd она не связана. В dialog_look есть конструкция:

v = par('^', v, n..' - '..

Вокруг тире здесь обычные пробелы, и из-за выравнивания по ширине начала многострочных фраз находятся на разных уровнях. Хотелось бы иметь возможность поставить вокруг тире неразрывные пробелы. Для этого можно:
1) Поправить stead (пока пользуюсь этим), но это временное решение, не позволит распространять программы для stead без движка. Вносить это изменение в trunk чревато тем, что stead перестанет полностью умещается в ASCII-диапазоне символов, станет уже в UTF-8. Не знаю, критично это или нет.
2) Скопипастить dialog_look в свой проект и заменить символы. Нехорошо, начинаем завязывать проект на недокументированные внутренности движка, плюс что если реализация dialog_look в stead поменяется?
3) Сделать какую-нибудь глобальную таблицу, куда включить строковую переменную или форматирующую строку для диалогов, например '%s - %s'. Возможно потом добавить такие же для других задач). Имхо, так себе решение, не гибко.
4) Поступить так же как с cmd, сделать функцию, например dialog_fmtphr, которую можно было бы переопределять при создании кастомизированных диалогов.

Последний вариант мне кажется наиболее адекватным. Чтобы не быть голословным, патч прилагается.

Post's attachments

Attachment icon dlgfmt.patch 1.22 kb, 162 downloads since 2010-01-17 

Re: Проблемы STEAD API

Надо подумать...

Re: Проблемы STEAD API

Посмотри svn, так подойдет?

20 (edited by Odyssey 2010-01-17 12:26:44)

Re: Проблемы STEAD API

Если честно, мне не нравится. Название функции enum подразумевает достаточно широкое использование. А что если нужно переопределить только форматирование фраз в диалоге, а enum к тому моменту уже вовсю будет использоваться в других частях stead? Получится что при переопределении вывода диалогов будут переопределены и эти "другие" части.

Т.е. я не против функции enum в принципе, но она для конкретной задачи, и её имя это отражает. И имхо, нужна отдельная функция для кастомизации вывода именно диалогов, которая в дефолтной реализации внутри может использовать enum.

Re: Проблемы STEAD API

Понимаю. Но пока не знаю как красиво вписать формат диалога в архитектуру.
В любом случае, enum лишним не будет, да и мне кажется что из самого enum можно детектить откуда он вызывается big_smile.

Еще подумаем...
P.S. Переименовал enum в txtnm. в iface оставил enum, для аналогии с txtc,b итд...

P.P.S формат надо вставлять в объект phrase. Но мне очень не нравится что в твоей версии он занимается переводом строки...

22 (edited by Odyssey 2010-01-17 13:24:11)

Re: Проблемы STEAD API

Над переводом строки я тоже задумывался. Но в конце концов решил, что если уж мы разрешаем переопределять форматирование фраз, лучше оставить этому методу полный контроль. Переопределение форматирования делается не от хорошей жизни и мало ли какую задачу там нужно будет решить. Может быть потребуется сделать диалог с двумя котроткими ответами, расположенными в одну строку. Поэтому тут не вижу смысла вносить жёсткое ограничение 1 фраза = 1 строка.

Другое дело то, что начальный и конечный перевод строки (для первой и последней фраз) -- действительно нехорошо вставлять при форматировании фраз, поскольку это уже не форматирование отдельных фраз, а форматирование блока фраз, и должно относится к уровню всего диалога. Поэтому можно передавать в функцию форматирования ещё и число фраз, чтобы можно было выяснить, не является ли текущая фраза первой или последней, типа:

if phnum == 0 then 
    return phnum..' - '..phtext;
else
    return '^'..phnum..' - '..phtext;
end

Re: Проблемы STEAD API

Peter wrote:

формат надо вставлять в объект phrase.

По хорошему - да. Но тогда нужно будет и в диалог вставить формат для списка фраз.

Re: Проблемы STEAD API

В общем, я пока не стал делать революционной смены api и развил старую идею.

В phrase реализовал look, который просто показывает фразу. (Как obj_look -- просто показывает объект).

dialog_look занимается форматированием вывода блока фраз, для этого он и был написан.

Выделять еще один уровень абстрактности, как некий непонятный fmt имхо нет смысла, правильный путь все-таки реализовывать свои look методы, а в диалоге он теперь очень прост. opairs меняться не будет, так что можно смело делать свою реализацию look и все. smile

Я понимаю, что это не совсем то, что тебе было нужно, но лучше я пока не придумал... Если идеи появятся -- вернемся еще раз.

Re: Проблемы STEAD API

Peter wrote:

Я понимаю, что это не совсем то, что тебе было нужно, но лучше я пока не придумал...

Ну почему же, то что получилось, меня вполне устраивает smile В dialog_look действительно остались только надёжные вызовы, его стало достаточно удобно переопределять. Плюс при переопределении мы получаем контроль над порядком вывода фраз, что логично на уровне диалога.

Единственный момент -- немного беспокоит двойная проверка фразы на disabled -- и в dialog_look, и в phrase_look.

И пользуясь случаем, а что такое game.hinting?