От модуля Main.tml остался довольно неоднозначный кусок: вроде и на 2 части слишком мало и на одну - слишком много. Решил всё-же не растягивать разбор на несколько десятков частей и ужал остатки в одну часть. Получилась она в стиле "галопом по Европам", но даже не смотря на это почти в два раза больше предыдущей части. Так-что садимся по удобнее и пытаемся додумать недосказанные места сами. Не всё же вам разжёвывать по словам
Дальше идут три вспомогательных функции. Функции очень похожи на методы, только они независимы от объектов и могут вызываться из любой части кода.
// ==================================================================== // действия // ==================================================================== // // функция для очистки лексем, возвращаемых парсером // очистить_лексему(Obj) { // личные и возвратные местоимения являются контекстно-зависимыми. // поэтому их нельзя использовать в контексте, отличном от того где они появились if("лич_мест; возвр_мест"==Obj.str) return set(Obj.obj, Obj.num, Obj.pos); //только объект+количество+позиция else return Obj //объект с полным набором парсерной информации }
Как мы уже знаем, объект может содержать информацию о количестве, морфологический ключ, текущее положение и прочие дополнительные поля значений. Практически все специальные поля значений (typ, str, num, obj, key, pos) - это некие приделанные к объекту дополнительные параметры. Так вот специальное поле .obj возвращает "чистый" объект без этих примесей.
Поле .pos возвращает физическое месторасположение объекта; тот объект, элементом (item) которого он является.
set() - функция, которая соединяет несколько параметров в один объект.
set(Obj.obj, Obj.num, Obj.pos) - добавляет к "чистому" объекту Obj его число и позицию. Т.е. удаляются ключ, строка и прочие примеси.
// // вспомогательная функция для сравнения ключей // check_keys(keyA, keyB, keys) { //keyA, keyB - строки со сравниваемыми ключами //keys - список сравниваемых ключей for(var i=1; i<=keys.len; i=i+1) { var A=keyA~keys[i] var B=keyB~keys[i] if(A and B and A!=B) return false //не совпало } return true //все совпало }
Функция check_keys(keyA, keyB, keys) сравнивает два ключа keyA и keyB по списку keys.
Например, check_keys("ИпЖрМеТыЕч", "ВыЕчИпМр", "рчп") сравнит ключи по роду, числу, и падежу, найдёт рассогласование в роде и возвратит false.
// // функция для проверки согласованности прилагательных // проверка_прил(Прил,Сущ,ЧегоЧье) //Прил,Сущ,ЧегоЧье - подставленные парсером объекты { var Сщ = очистить_лексему(Сущ) //получим очищенное существительное if(Прил) //прилагательное { var Пр = очистить_лексему(Прил) //получим очищенное прилагательное if(Прил.str=="прит_") //это притяжательное { //отсеиваем классы if(Прил.ctg=="class") return "класс в роли притяжательного!" //проверим Сущ if("лич_мест; возвр_мест;"==Сущ.str) //местоимения с притяжательными не используются! return "что {Прил.lex*Прил.key}?" //проверим согласование Сущ и Прил if(Прил.str=="прит_мест" and Прил.key~л=="3л") var keys="чпд" //род притяжательных местоимений в 3м лице согласуется с владельцем а не предметом! else var keys="чпрд" //Число, Падеж, Род, оДушевленность if(!check_keys(Прил.key,Сущ.key,keys)) return "разве так говорят - '{Пр:притяжательное*Прил.key} {Сщ.lex*Сущ.key}'?" //владелец и объект должны различаться if(Прил.obj==Сущ.obj) return "{Пр:притяжательное} {Сщ.lex} - сам себе свой?" //притяжательное должно указывать на владельца объекта if(Сущ.чей!=Прил.obj and !Прил.item[Сущ]) return "у {Пр*РпНе} нет {Сщ.lex*Рп}." } else //это обычное прилагательное { //проверим Сущ if("лич_мест; возвр_мест;"==Сущ.str) //местоимения с прилагательными не используются! return "что {Пр.lex*Прил.key}?" //проверим согласование Сущ и Прил if(!check_keys(Прил.key,Сущ.key,"чпрд")) //число, падеж, род, одушевленность return "разве так говорят - '{Пр.lex*Прил.key} {Сщ.lex*Сущ.key}'?" //прилагательное должно указывать на тот-же объект что и существительное if(Прил.obj!=Сущ.obj) return "тут нет {Пр.lex*Рп*Сщ.lex~рч} {Сщ.lex*Рп}." } } if(ЧегоЧье) //определение вещества или владельца { //например: моток веревки, кирка рудокопа if("лич_мест; возвр_мест; это"==ЧегоЧье.str or ЧегоЧье.str=="прит_") return "местоимения тут недопустимы!" //проверим Сущ if("лич_мест; возвр_мест;"==Сущ.str) //местоимения с указанием вещества/владельца не используются! return "что {ЧегоЧье.lex*ЧегоЧье.key}?" if(ЧегоЧье.obj==Сущ.obj) //лексема этого-же объекта { if(ЧегоЧье.str!="из_чего" and ЧегоЧье.str!="чей") //не является лексемой "из_чего" или "чей" return "разве так говорят - '{Сщ.lex*Сущ.key} {ЧегоЧье.lex*ЧегоЧье.key}'?" } if(Сщ.lex*Рп==ЧегоЧье.lex*Рп) return "{Сщ.lex} {ЧегоЧье.lex*Рп}? A масло то масленное?!" if(Сущ.из_чего) if(Сущ.из_чего==ЧегоЧье.obj) return "" //совпало с объектом "из_чего" if(Сущ.чей) if(ЧегоЧье.obj==Сущ.чей) return "" //совпало с объектом "чей" if(Сущ.pos==ЧегоЧье) return "" //совпало с расположением return "у {ЧегоЧье.lex*Рп} нет {Сщ.lex*Рп}." } }
(Прил.str=="прит_") и ("лич_мест; возвр_мест;"==Сущ.str) - мы уже не первый раз видим, но ненавязчиво обходим стороной тот факт, что сравнение строк в ТОМе происходит не совсем обычно. Пора, наверное, разобраться с этим и сделать неутешительный вывод: при сравнении строк X==Y проверяется наличие подстроки Y в строке X. Это жутко сбивает с толку неподготовленный глаз. Как же тогда сделать полное сравнение двух строк? Самый простой способ, который я вижу - это двойное сравнение: (X==Y)и(Y==X). Вот пока так. Надеюсь, ASBer подскажет лучший способ.
Если присмотреться, то смысл функции довольно прозрачен, но на всякий случай расшифрую с примерами:
Если задано прилагательное, то проверяем конструкцию прилагательное-существительное ("мамина тарелка").
Сначала идёт проверка притяжательных прилагательных "if(Прил.str=="прит_") //это притяжательное":
Часть "отсеиваем классы" не позволяет использовать классы в роли прилагательного.
Часть "проверим Сущ" блокирует личные и возвратные местоимения:
Здесь лежит мамина тарелка
> возьми мамину её
что мамину?
Часть "проверим согласование Сущ и Прил" обрабатывает несогласованность по числу, падежу, роду, одушевленности:
> возьми мамины тарелку
разве так говорят - 'мамины тарелку'?
Часть "владелец и объект должны различаться" обрабатывает такие казусы:
> осмотри мамину маму
мамина мама - сам себе свой?
Часть "притяжательное должно указывать на владельца объекта" проверяет наличие у объекта этого существительного:
> осмотри мамин карандаш
у мамы нет карандаша.
Затем - проверка обычных прилагательных "else //это обычное прилагательное":
"проверим Сущ" не позволяет использовать прилагательные с местоимениями:
> осмотри большого себя
что большого?
"проверим согласование Сущ и Прил" аналогично с притяжательными.
"прилагательное должно указывать на тот-же объект что и существительное":
> осмотри умного крота
тут нет умного крота
Часть "если задано ЧегоЧье". Дальше всё идёт по аналогии.
//------------------------------------------------------------------------- // // действия "осмотреть" и "осмотреться" // action осмотрелся { this.инфинитив = "осмотреться" pat = "о=осм=осмотреться=осмотрись=оглядеться=оглядись" pat = "о=осм=осмотреться=осмотрись=оглядеться=оглядись вокруг=тут=здесь" this.инфинитив = "осмотреться" title = "осм%;; МрЕч2лПв; //сочетает осмотреться и осматриваться МрЕчПв=отрелся; ЖрЕчПв=отрелась; СрЕчПв=отрелось; МчПв=отрелись; //прошедшее время Еч1лНв=атриваюсь; Еч2лНв=атриваешься; Еч3лНв=атривается; //настоящее время Мч1лНв=атриваемся; Мч2лНв=атриваетесь; Мч3лНв=атриваются; Еч1лНв=отрюсь; Еч2лНв=отришься; Еч3лНв=отрятся; //будущее время Мч1лНв=отримся; Мч2лНв=отритесь; Мч3лНв=отрятся;" CmbChk() { if(!actor.персонаж) return "" //внеигровая ситуация //проверим, видно ли локацию return нельзя_увидеть(actor.loc,actor) } }
action (действие) - категория объектов, представляющих из себя некие проверки, позволяющие игре срабатывать на определённые фразы, введённые игроком. Каждый объект action срабатывает на узкий набор команд, подходящих по заданным шаблонам. Вместе они образуют всё пространство команд, на которые игра может адекватно реагировать.
Спектр фраз, на которые срабатывает действие, задаётся в виде шаблонов pat (шаблон). Можно задать неограниченное кол-во шаблонов, последовательно присваивая их свойству pat (шаблон). Прочитать свойство pat не получится - оно только для записи.
В строке шаблона можно задавать синонимы слов через знак "=".
Например, шаблон "о=осм=осмотреться=осмотрись=оглядеться=оглядись вокруг=тут=здесь" будет срабатывать на фразы: "осм здесь", "оглядеться тут", "о вокруг" и др.
Здесь небольшая ошибочка в библиотеке: this.инфинитив = "осмотреться" определено 2 раза. Лишнее можете убрать
Метод CmbChk() вызывается каждый раз, когда срабатывает какой-либо шаблон в действии и служит для дополнительной проверки.
персонаж.осмотрелся() { осмотрел(loc) }
Это вынесенный метод осмотрелся() объекта персонаж. Как вы уже знаете, через точку можно обращаться к различным свойствам и методам объекта. Так-же через точку можно объявлять свойства и методы вне тела объекта.
так, объявление
unique персонаж
{
осмотрелся()
{
}
}
и
unique персонаж
{
}персонаж.осмотрелся()
{
}
абсолютно идентичны.
Объект категории action "осмотрелся", срабатывая, ищет у объекта, совершающего действие (для наших команд это будет pers) одноимённый метод, и если находит, то выполняет его.
В данном случае, если мы введём команду "осм", то сработает первый шаблон объекта осмотрелся и, пройдя проверку CmbChk(), вызовет метод pers.осмотрелся(). Внутри этого метода задан вызов другого метода осмотрел(loc), который идёт ниже.
метод нельзя_увидеть() принадлежит классу engine, который находится в модуле Physics.tml и представляет из себя физический движок. Мы его не рассматриваем, т.к. он не используется в игре "Мышки".
"осмотреть"="осмотри"="исследовать"="исследуй" action осмотрел { this.инфинитив = "осмотреть" title = "осм%;; МрЕч2лПв; //осмотреть - осматривать МрЕчПв=отрел; ЖрЕчПв=отрела; СрЕчПв=отрело; //прошедшее время МчПв=отрели; Еч1лНв=атриваю; Еч2лНв=атриваешь; Еч3лНв=атривает; //настоящее время Мч1лНв=атриваем; Мч2лНв=атриваете; Мч3лНв=атривают; Еч1лНв=отрю; Еч2лНв=отришь; Еч3лНв=отрят; //будущее время Мч1лНв=отрим; Мч2лНв=отрите; Мч3лНв=отрят;" pat = "о=осм=осмотреть @Что:описание*ВпСи" pat = "о=осм=осмотреть &Какое*ВпПи @Что:описание*ВпСи" pat = "о=осм=осмотреть @Что:описание*ВпСи &Какое*ВпПи" pat = "о=осм=осмотреть @Что:описание*ВпСи &ЧегоЧье*РпНеНм" pat = "о=осм=осмотреть &Какое*ВпПи @Что:описание*ВпСи &ЧегоЧье*РпНеНм" pat = "о=осм=осмотреть @Что:описание*ВпСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat = "о=осм=осмотреть &Какое*ВпПи @Что:описание*ВпСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat = "@Что:описание*ИпСи" //команда из одного слова = осмотри 'предмет' Что(Что) { if(!actor.персонаж) return "" //внеигровая ситуация //проверим, видно ли этот объект return нельзя_увидеть(Что,actor) } CmbChk(Какое,Что,Какого,ЧегоЧье) { if(ЧегоЧье) { //проверим прилагательное к ЧегоЧье var S = проверка_прил(Какого,ЧегоЧье) if(S) return S } return проверка_прил(Какое,Что,ЧегоЧье) } }
В шаблонах могут указываться переменные:
@ - символ, указывающий, что следующее за ним слово - это переменная, означающая объект, присутствующий в текущей локации.
Всего существует 5 специальных символов, обозначающих переменные в шаблонах: @, ~, &, # и $
Что - переменная, в которую помещается объект при срабатывании шаблона
"@Что:описание*ВпСи" означает, что шаблон будет срабатывать, если на месте переменной стоит слово [С]уществительное в [В]инительном падеже (*ВпСи), означающее объект в текущей локации (@) с непустым свойством "описание (:описание)". Если всё это подходит, то шаблон срабатывает и в переменную Что записывается этот объект.
& - определяет объекты, независимо от их местонахождения;
"&Какое*ВпПи" срабатывает на все [П]рилагательные в [В]инительном падеже.
Если в действии определён метод, имя которого совпадает с именем переменной в шаблоне, то этот метод вызывается для дополнительной проверки этой переменной.
У нас указан метод Что(Что) и в шаблонах используется переменная Что. Это значит, что при срабатывании шаблона, перед вызовом CmbChk будет вызван метод Что(Что). И только если он вернёт пустое значение, то шаблон будет считаться подходящим и пойдёт проверка CmbChk.
персонаж.осмотрел(Что) { if(told) { if(свобода_воли(Что)==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему addressee = told if(Что.typ=="object") { if(Что.полное_описание && !Что.уже_осмотрено) { this > "{Что.полное_описание}." Что.уже_осмотрено = да } else this > "{Что.st_описание}." Что.осмотрено() } else this > "%{Что}." //строка или любой другой простой тип } else { if(Что.typ=="object") { if(Что.полное_описание && !Что.уже_осмотрено) { if(this==pers) %{Что.полное_описание}. else this > "{Что.полное_описание}." Что.уже_осмотрено = да } else if(this==pers) %{Что.st_описание}. else this > "{Что.st_описание}." Что.осмотрено() } else %{Что}. //строка или любой другой простой тип } }
Метод осмотрел(Что) объекта персонаж обрабатывает команды осмотреть что-либо.
told - персонаж, произнёсший предыдущую речь.
Оператор > после объекта, обозначающего персонаж, выводит текст на экран от лица этого персонажа. Отличается от % не только специальным оформлением, но и тем, что парсер воспринимает произнесённую фразу и обрабатывает её. Т.е. на неё могут отреагировать другие персонажи.
Здесь меня берут сомнения на счёт использования % в строке "else this > "%{Что}." ".
Ещё, почему везде используется фраза от лица this: this > "...", а в самом конце мы выводим текст непосредственно на экран: else %{Что}. ?
Но мы не будем зацикливаться на этом и пойдём дальше.
//------------------------------------------------------------------------- action инвентарь { pat = "инв=инвентарь" } персонаж.инвентарь() { if(told) { if(свобода_воли()==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему if(item:показывать_в_инвентаре.num) this > "у {this*РпНе} есть {item:показывать_в_инвентаре}." else this > "у {this*РпНе} ничего нет." } else if(item:показывать_в_инвентаре.num) %у {this*РпНе} есть {item:показывать_в_инвентаре}. else %у {this*РпНе} ничего нет. }
Ещё одна пара действие-метод. Обрабатывает команды осмотра инвентаря.
//------------------------------------------------------------------------- "иди"="войди"="войти"="идти" //"иди" также используется в action ушел_по_направлению action вошел { this.инфинитив="войти" title = "%;; МрЕч2лПвТы; //войти-входить ТыМрЕчПв=вошёл; ТыЖрЕчПв=вошла; ТыСрЕчПв=вошло; //прошедшее на ты Вы1лМрЕчПв=вошёл; Вы1лЖрЕчПв=вошла; Вы1лСрЕчПв=вошло; //прошедшее на вы Вы2лЕчПв=вошли; Вы3лМрЕчПв=вошёл; Вы3лЖрЕчПв=вошла; Вы3лСрЕчПв=вошло; МыЕчПв=вошли; //прошедшее на мы МчПв=вошли; ТыЕч1лНв=вхожу; ТыЕч2лНв=входишь; ТыЕч3лНв=входит; //настоящее на ты ВыЕч1лНв=вхожу; ВыЕч2лНв=входите; ВыЕч3лНв=входит; //настоящее на вы МыЕч1лНв=входим; МыЕч2лНв=входите; МыЕч3лНв=входят; //настоящее на мы Мч1лНв=входим; Мч2лНв=входите; Мч3лНв=входят; ТыЕч1лБв=войду; ТыЕч2лБв=войдёшь; ТыЕч3лБв=войдёт; //будущее на ты ВыЕч1лБв=войду; ВыЕч2лБв=войдёте; ВыЕч3лБв=войдёт; //будущее на вы МыЕч1лБв=войдём; МыЕч2лБв=войдёте; МыЕч3лБв=войдут; //будущее на мы Мч1лБв=войдём; Мч2лБв=войдёте; Мч3лБв=войдут;" pat="иди $Предлог &Куда:можно_войти*ВпСи" pat="иди к &Куда:можно_войти*ДпСи" Куда(Куда) { //подчистим личные и возвратные местоимения Куда = очистить_лексему(Куда) //проверим возможность прохода var Error = невозможно_пройти(Куда,actor) if(Error) { if(Error=="здесь нет") estimate = 40 //ошибка на уровне object_not_found return Error } } } персонаж.вошел(Куда) { Куда = очистить_лексему(Куда) if(told) { if(свобода_воли(,Куда)==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему } Куда + this //сообщение см. в событиях }
$ - строковая переменная.
estimate = 40 - нечто недокументированное или находящееся глубоко в других модулях. В любом случае это нас не касается, т.к. метод невозможно_пройти описан в Physics.tml, который мы не рассматриваем.
Куда + this - оператор "+", применённый к двум объектам, перемещает объект справа в объект слева. Если перемещаемый объект - это локация или уникальное, то он автоматически убирается из объекта, где находился до этого. Аналогично этому, оператор "-" убирает объект справа из объекта слева. Убираемый объект помещается в глобальный объект global, заданный в платформе жёстко и хранящий в себе все глобальные переменные, функции и нигде-не-находящиеся объекты.
//------------------------------------------------------------------------- //action вышел //{ this.инфинитив="выйти" // pat="выйти=выйди" // pat="выйти=выйди $Предлог &Откуда:можно_войти*РпСи" // // CmbChk(Откуда) // { // if(!Откуда) Откуда = actor.loc // if(!Откуда.выход) // return "{Откуда.пред_из} {Откуда*Рп} нет выхода!" // // } //} //персонаж.вышел(Откуда) //{ Откуда = очистить_лексему(Откуда) // if(!Откуда) Откуда = loc // if(told) // { if(свобода_воли(,Откуда)==нет) // return //персонаж отказался выполнить действие, или выполнил его по-своему // } // Откуда.выход + this //} //-------------------------------------------------------------------------
Закомментированное действие "вышел". Видимо автор библиотеки обнаружил глюки в его работе. Можете разобрать и выяснить на досуге
//"возьми"="взять"="брать"="бери"="забрать"="забери"="поднять"="подними" action взял { this.инфинитив="взять" title = "%;; МрЕч2лПвТы; //взять-брать ТыМрЕчПв=взял; ТыЖрЕчПв=взяла; ТыСрЕчПв=взяло; //прошедшее время на ты Вы1лМрЕчПв=взял; Вы1лЖрЕчПв=взяла; Вы1лСрЕчПв=взяло; //прошедшее время на вы Вы2лЕчПв=взяли; Вы3лМрЕчПв=взял; Вы3лЖрЕчПв=взяла; Вы3лСрЕчПв=взяло; МыЕчПв=взяли; //прошедшее время на мы МчПв=взяли; ТыЕч1лНв=беру; ТыЕч2лНв=берёшь; ТыЕч3лНв=берёт; //настоящее время на ты ВыЕч1лНв=беру; ВыЕч2лНв=берёте; ВыЕч3лНв=берёт; //настоящее время на вы МыЕч1лНв=берём; МыЕч2лНв=берёте; МыЕч3лНв=берут; //настоящее время на мы Мч1лНв=берём; Мч2лНв=берёте; Мч3лНв=берут; ТыЕч1лБв=возьму; ТыЕч2лБв=возьмёшь; ТыЕч3лБв=возьмёт; //будущее время на ты ВыЕч1лБв=возьму; ВыЕч2лБв=возьмёте; ВыЕч3лБв=возьмёт; //будущее время на вы МыЕч1лБв=возьмём; МыЕч2лБв=возьмёте; МыЕч3лБв=возьмут; //будущее время на мы Мч1лБв=возьмём; Мч2лБв=возьмёте; Мч3лБв=возьмут;" // лексемы глаголов // Гл - глагол // Иф - инфинитив // Пф - повелительная форма this.возьми = "%; Гл; Иф; Иф=взять; Пф=возьми; Пф=возьмите;" this.бери = "%; Гл; Иф; Иф=брать; Пф=бери; Пф=берите;" this.забери = "за%; Гл; Иф; Иф=брать; Пф=бери; Пф=берите;" this.подними = "подн%; Гл; Иф; Иф=ять; Пф=ими; Пф=имите;" pat="&Глагол*Гл @Что:можно_взять*РпВуСи"//для вещественного уточнения родительного падежа pat="&Глагол*Гл @Что:можно_взять*РпМчСи"//для мн.числа родительного падежа pat="&Глагол*Гл @Что:можно_взять*ВпСи" pat="&Глагол*Гл &Какое*РпПи @Что:можно_взять*РпВуСи" pat="&Глагол*Гл &Какое*РпПи @Что:можно_взять*РпМчСи" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*ВпСи" pat="&Глагол*Гл @Что:можно_взять*РпВуСи &Какое*РпПи" pat="&Глагол*Гл @Что:можно_взять*РпМчСи &Какое*РпПи" pat="&Глагол*Гл @Что:можно_взять*ВпСи &Какое*ВпПи" pat="&Глагол*Гл @Что:можно_взять*РпВуСи &ЧегоЧье*Рп" pat="&Глагол*Гл @Что:можно_взять*РпМчСи &ЧегоЧье*Рп" pat="&Глагол*Гл @Что:можно_взять*ВпСи &ЧегоЧье*Рп" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*РпВуСи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*РпМчСи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*ВпСи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл @Что:можно_взять*РпВуСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл @Что:можно_взять*РпМчСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл @Что:можно_взять*ВпСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*РпВуСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*РпМчСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*ВпСи &Какого*РпПи &ЧегоЧье*РпНеНм" pat="&Глагол*Гл @Что:можно_взять*РпВуСи $Предлог @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*РпМчСи $Предлог @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*ВпСи $Предлог @Откуда*РпСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи @Что:можно_взять*РпВуСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи @Что:можно_взять*РпМчСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи @Что:можно_взять*ВпСи" pat="&Глагол*Гл &Какое*РпПи @Что:можно_взять*РпВуСи $Предлог @Откуда*РпСи" pat="&Глагол*Гл &Какое*РпПи @Что:можно_взять*РпМчСи $Предлог @Откуда*РпСи" pat="&Глагол*Гл &Какое*ВпПи @Что:можно_взять*ВпСи $Предлог @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*РпВуСи &Какое*РпПи $Предлог @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*РпМчСи &Какое*РпПи $Предлог @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*ВпСи &Какое*ВпПи $Предлог @Откуда*РпСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи &Какое*ВпПи @Что:можно_взять*РпВуСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи &Какое*РпПи @Что:можно_взять*РпМчСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи &Какое*ВпПи @Что:можно_взять*ВпСи" pat="&Глагол*Гл $Предлог @Откуда*РпСи @Что:можно_взять*РпВуСи &Какое*РпПи" pat="&Глагол*Гл $Предлог @Откуда*РпСи @Что:можно_взять*РпМчСи &Какое*РпПи" pat="&Глагол*Гл $Предлог @Откуда*РпСи @Что:можно_взять*ВпСи &Какое*ВпПи" pat="&Глагол*Гл @Что:можно_взять*РпВуСи $Предлог &КакоеОткуда*РпПи @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*РпМчСи $Предлог &КакоеОткуда*РпПи @Откуда*РпСи" pat="&Глагол*Гл @Что:можно_взять*ВпСи $Предлог &КакоеОткуда*РпПи @Откуда*РпСи" pat="&Глагол*Гл $Предлог &КакоеОткуда*РпПи @Откуда*РпСи @Что:можно_взять*РпВуСи" pat="&Глагол*Гл $Предлог &КакоеОткуда*РпПи @Откуда*РпСи @Что:можно_взять*РпМчСи" pat="&Глагол*Гл $Предлог &КакоеОткуда*РпПи @Откуда*РпСи @Что:можно_взять*ВпСи"
Очень много шаблонов. Будем надеяться, что в будущих версиях ТОМа с этим будет попроще.
Давайте отвлечёмся от трудностей и повторим лексемы.
Как можно короче описать лексему "%; Гл; Иф; Иф=взять; Пф=возьми; Пф=возьмите;"?
Вот так: "возьм%; Гл; Иф; Иф<взять; Пф=и; Пф=ите"
А "%; Гл; Иф; Иф=брать; Пф=бери; Пф=берите;"?
Аналогично: "бери%; Гл; Иф; Иф<брать; Пф=; Пф=те"
"Подумаешь, какие-то 4-5 символов" - скажете вы. А если будет не 3, а 9 или 20 форм? В общем, не забывайте о значке "<".
Глагол(Глагол) { //проверка глагола if(Глагол!=this) { estimate = 1; //ошибка на уровне is_not_recognized return "это мне непонятно^^^" } } Что(Что) { //проверим, видно ли этот объект var error = нельзя_увидеть(Что,actor) if(error) return error //проверим доступность return нельзя_дотронуться(Что,actor) } Откуда(Откуда) { //проверим, видно ли этот объект var error = нельзя_увидеть(Откуда,actor) if(error) return error //проверим доступность return нельзя_дотронуться(Откуда,actor) }
Дополнительные проверки переменных из шаблонов.
CmbChk(Какое,Что,ЧегоЧье,КакоеОткуда,Откуда,ЧегоЧьеОткуда,Предлог) { //проверим прилагательные var Error = проверка_прил(Какое,Что,ЧегоЧье) if(Error) return Error Error = проверка_прил(КакоеОткуда,Откуда,ЧегоЧьеОткуда) if(Error) return Error //подчистим личные и возвратные местоимения Что = очистить_лексему(Что) Откуда = очистить_лексему(Откуда) var Согласование = "ПпМуНе" //предложный местный падеж но не возвратное местоимение //проверка места if(Откуда) { //проверка предлога Error = Откуда.можно_изъять(Предлог,Что) //для предметов if(Error) return Error Error = Откуда.может_не_дать(Что) //для персонажей if(Error) return Error //проверка наличия if(Что.pos!=Откуда) { if("у"==Откуда.предлог) Согласование = "РпНе" //требует родительный падеж return "{Откуда.предлог} {Откуда.lex*Согласование} нет {Что.lex*Рп}." } } //проверка количества if(Что.num>Что.pos.item[Что].num) { if("у"==Что.pos.предлог) Согласование = "РпНе"//требует родительный падеж return "{Что.pos.предлог} {Что.pos*Согласование} нет столько {Что*Рп}." } //проверка на собственность if(Что.pos==actor) return "{Что.lex} уже у {actor*РпНе}." //проверка на свободу воли персонажей Error = Что.pos.может_не_дать(Что) if(Error) return Error } }
Проверка CmbChk. Всё предельно ясно и прокомментировано.
персонаж.взял(Что) { Что = очистить_лексему(Что) if(told) { if(свобода_воли(Что)==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему } if(Что.pos.не_дал(Что)==нет) return //владелец не дал нам то, что мы хотели взять var Где = Что.pos //запомним место this + Что if(item[Что]) if(Где==loc) %{this} {взял*this} {Что.lex*Вп}. else %{this} {взял*this} {Что.lex*Вп} {Где.пред_из[Что]} {Где*Рп}. }
Что - это предмет, который пытаются взять.
Что.pos - объект, в котором находится этот предмет (в случае с персонажем означает, что предмет находится у него в инвентаре)
Что.pos.не_дал(Что) - метод персонажа, в инвентаре которого лежит предмет, который хотят взять.
%{this} {взял*this} {Что.lex*Вп} {Где.пред_из[Что]} {Где*Рп}. - какие конструкции! Ни одного чистого слова. Всё генерируется платформой. Вот прелесть ТОМа, которая заставила меня остаться поклонником этой платформы.
Эта строчка генерирует примерно такие фразы: "Кошка взяла мышку из мышеловки."
//------------------------------------------------------------------------- "положи"="положить"="выложить"="выложи"="бросить"="брось" action положил { this.инфинитив="положить" title = "%;; МрЕч2лПвТы; //положить-класть ТыМрЕчПв=положил; ТыЖрЕчПв=положила; ТыСрЕчПв=положило; //прошедшее на ты Вы1лМрЕчПв=положил; Вы1лЖрЕчПв=положила; Вы1лСрЕчПв=положило; //прошедшее на вы Вы2лЕчПв=положили; Вы3лМрЕчПв=положил; Вы3лЖрЕчПв=положила; Вы3лСрЕчПв=положило; МыЕчПв=положили; //прошедшее на мы МчПв=положили; //прошедшее мн.число ТыЕч1лНв=кладу; ТыЕч2лНв=кладёшь; ТыЕч3лНв=кладёт;//настоящее на ты ВыЕч1лНв=кладу; ВыЕч2лНв=кладёте; ВыЕч3лНв=кладёт;//настоящее на вы МыЕч1лНв=кладём; МыЕч2лНв=кладёте; МыЕч3лНв=кладут;//настоящее на мы Мч1лНв=кладём; Мч2лНв=кладёте; Мч3лНв=кладут; //настоящее мн.число ТыЕч1лБв=положу; ТыЕч2лБв=положишь; ТыЕч3лБв=положит; //будущее на ты ВыЕч1лБв=положу; ВыЕч2лБв=положите; ВыЕч3лБв=положит; //будущее на вы МыЕч1лБв=положим; МыЕч2лБв=положите; МыЕч3лБв=положат; //будущее на мы Мч1лБв=положим; Мч2лБв=положите; Мч3лБв=положат;" //будущее мн.число pat="положи @Что:можно_взять*РпСи"//тут необходима доп.проверка, т.к. Рп подходит не ко всем существительным pat="положи @Что:можно_взять*ВпСи" pat="положи @Что:можно_взять*РпСи $Предлог @Куда:предмет*ВпСи" pat="положи @Что:можно_взять*ВпСи $Предлог @Куда:предмет*ВпСи" Что(Что) { //проверим наличие предмета if(Что.pos!=actor) { Что = очистить_лексему(Что) return "у {actor*РпНе} нет {Что.lex*Рп}." } } Куда(Куда) { //проверим, видно ли этот объект var error = нельзя_увидеть(Куда,actor) if(error) return error //проверим доступность return нельзя_дотронуться(Куда,actor) } CmbChk(Какое,Что,ЧегоЧье,КакоеКуда,Куда,ЧегоЧьеКуда,Предлог) { if(!Что) { //еще тут должна быть подстановка последненего предмета... return "что {act.инфинитив}?" } //проверим прилагательные var S = проверка_прил(Какое,Что,ЧегоЧье) if(S) return S S = проверка_прил(КакоеКуда,Куда,ЧегоЧьеКуда) if(S) return S //подчистим личные и возвратные местоимения Что = очистить_лексему(Что) Куда = очистить_лексему(Куда) //проверим место if(Куда) { var S = Куда.можно_поместить(Предлог,Что) if(S) return S } } } персонаж.положил(Что,Куда) { Что = очистить_лексему(Что) if(told) { if(свобода_воли(Что,Куда)==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему } if(Куда) { Куда = очистить_лексему(Куда) Куда + Что if(Куда.item[Что]) //удалось положить %{this} {act*this} {Что.lex*Вп} {Куда.пред_в} {Куда*Вп}. } else { loc + Что if(loc.item[Что]) //удалось положить %{this} {act*this} здесь {Что.lex*Вп}. //просто положил на пол или что там вместо пола... } } //------------------------------------------------------------------------- action дал { this.инфинитив="дать" title = "да%;; МрЕч2лПвТы; //дать-давать ТыМрЕчПв=л; ТыЖрЕчПв=ла; ТыСрЕчПв=ло; //прошедшее на ты Вы1лМрЕчПв=л; Вы1лЖрЕчПв=ла; Вы1лСрЕчПв=ло; //прошедшее на вы Вы2лЕчПв=ли; Вы3лМрЕчПв=л; Вы3лЖрЕчПв=ла; Вы3лСрЕчПв=ло; МыЕчПв=ли; //прошедшее на мы МчПв=ли; //прошедшее мн.число ТыЕч1лНв=ю; ТыЕч2лНв=ёшь; ТыЕч3лНв=ёт; //настоящее на ты ВыЕч1лНв=ю; ВыЕч2лНв=ёте; ВыЕч3лНв=ёт; //настоящее на вы МыЕч1лНв=ём; МыЕч2лНв=ёте; МыЕч3лНв=ют; //настоящее на мы Мч1лНв=ём; Мч2лНв=ёте; Мч3лНв=ют; //настоящее мн.число ТыЕч1лБв=м; ТыЕч2лБв=шь; ТыЕч3лБв=ст; //будущее на ты ВыЕч1лБв=м; ВыЕч2лБв=дите; ВыЕч3лБв=ст; //будущее на вы МыЕч1лБв=дим; МыЕч2лБв=дите; МыЕч3лБв=дут;//будущее на мы Мч1лБв=дим; Мч2лБв=дите; Мч3лБв=дут;" //будущее мн.число pat="дай @Что:можно_взять*РпВуСи @Кому:персонаж*ДпСи" pat="дай @Что:можно_взять*РпМчСи @Кому:персонаж*ДпСи" pat="дай @Что:можно_взять*ВпСи @Кому:персонаж*ДпСи" pat="дай @Кому:персонаж*ДпСи @Что:можно_взять*РпВуСи" pat="дай @Кому:персонаж*ДпСи @Что:можно_взять*РпМчСи" pat="дай @Кому:персонаж*ДпСи @Что:можно_взять*ВпСи" pat = "дай @Кому:персонаж*ДпСи @Что:можно_взять*РпВуСи &Какое*РпПи" pat = "дай @Кому:персонаж*ДпСи @Что:можно_взять*РпМчСи &Какое*РпПи" pat = "дай @Кому:персонаж*ДпСи @Что:можно_взять*ВпСи &Какое*ВпПи" pat = "дай @Кому:персонаж*ДпСи &Какое*РпПи @Что:можно_взять*РпВуСи" pat = "дай @Кому:персонаж*ДпСи &Какое*РпПи @Что:можно_взять*РпМчСи" pat = "дай @Кому:персонаж*ДпСи &Какое*ВпПи @Что:можно_взять*ВпСи" pat = "дай @Кому:персонаж*ДпСи @Что:можно_взять*РпВуСи &ЧегоЧье*РпНеНм" pat = "дай @Кому:персонаж*ДпСи @Что:можно_взять*РпМчСи &ЧегоЧье*РпНеНм" pat = "дай @Кому:персонаж*ДпСи @Что:можно_взять*ВпСи &ЧегоЧье*РпНеНм" Что(Что) { //проверим, видно ли этот предмет var error = нельзя_увидеть(Что,actor) if(error) return error //проверим доступность error = нельзя_дотронуться(Что,actor) if(error) return error //проверим, дадут ли нам это if(Что.pos!=actor) { error = Что.pos.может_не_дать(Что) if(error) return error } } Кому(Кому) { //проверим, видно ли этого персонажа var error = нельзя_увидеть(Кому,actor) if(error) return error //проверим доступность return нельзя_дотронуться(Кому,actor) } CmbChk(Какое,Что,ЧегоЧье,КакомуКому,Кому,ЧегоЧьеКому) { //проверим прилагательные var Error = проверка_прил(Какое,Что,ЧегоЧье) if(Error) return Error Error = проверка_прил(КакомуКому,Кому,ЧегоЧьеКому) if(Error) return Error //подчистим личные и возвратные местоимения Что = очистить_лексему(Что) Кому = очистить_лексему(Кому) //персонаж может не взять то что ему дают Error = Кому.может_не_взять(Что) if(Error) return Error //проверим место if(Что.pos==Кому) return "у {Кому.lex*ВпНе} уже есть {Что.lex*Ип}" } } персонаж.дал(Что,Кому) { Что = очистить_лексему(Что) Кому = очистить_лексему(Кому) if(told) { if(свобода_воли(Что,Кому)==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему } var Где = Что.pos //запомним место if(Где!=this) //у тебя нет Что { if(Что.pos.не_дал(Что)==нет) return //владелец отказался дать тебе Что this + Что //сначала берем Что себе if(item[Что])//успешно { %{this} {взял*this} {Что.lex*Вп} {Где.пред_из} {Где*Рп} Что = set(Что.obj, Что.num, this) } else return //неудачно } if(Кому.не_взял(Что)==нет) return //Кому отказался брать Что Кому + Что if(Кому.item[Что]) %{this} {дал*this} {Кому*Дп} {Что.lex*Вп}. }
Аналогично предыдущим действиям.
//------------------------------------------------------------------------- // // фраза - команда персонажу // phrase команда { //ВНИМАНИЕ! здесь звательный падеж есть даже у прилагательного!!! // проще задать Зп для прилагательных, чем отлавливать несогласование между ИпПи и ЗпСи pat="&Какой*ЗпПи @Addressee:персонаж*ЗпСи $L1" pat="&Какой*ЗпПи @Addressee:персонаж*ЗпСи $L1 $L2" pat="@Addressee:персонаж*ЗпСи $L1" pat="@Addressee:персонаж*ЗпСи $L1 $L2" CmbChk(Какой,Addressee) { return проверка_прил(Какой,Addressee) } }
phrase (фраза) - категория, очень похожая на action, только обрабатывает не команды, а фразы самих персонажей.
персонаж.команда(L1,L2) { //...здесь можно вставить реакцию на команды другим персонажам... if(this!=addressed) return; //не нас просят if(this==told) this > "вот, уже сам с собой начал говорить^^^" //политес if(L1=="пожалуйста") { global.вежливая_просьба = да //может пригодицца L1=L2 L2="" } else global.вежливая_просьба = нет //пытаемся выполнить команду var Error = this < L1 + " " + L2 if(Error) this > Error } //------------------------------------------------------------------------- // // действие - сказать // action сказал { this.инфинитив="сказать" title = "ска%;; МрЕч2лПвТы; //сказать/говорить ТыМрЕчПв=зал; ТыЖрЕчПв=зала; ТыСрЕчПв=зало; //прошедшее на ты Вы1лМрЕчПв=зал; Вы1лЖрЕчПв=зала; Вы1лСрЕчПв=зало; //прошедшее на вы Вы2лЕчПв=зали; Вы3лМрЕчПв=зал; Вы3лЖрЕчПв=зала; Вы3лСрЕчПв=зало; МыЕчПв=зали; //прошедшее на мы МчПв=зали; //прошедшее мн.число ТыЕч1лНв<говорю; ТыЕч2лНв<говоришь; ТыЕч3лНв<говорит; //настоящее на ты ВыЕч1лНв<говорю; ВыЕч2лНв<говорите; ВыЕч3лНв<говорит; //настоящее на вы МыЕч1лНв<говорим; МыЕч2лНв<говорите; МыЕч3лНв<говорят; //настоящее на мы Мч1лНв<говорим; Мч2лНв<говорите; Мч3лНв<говорят; //настоящее мн.число ТыЕч1лБв=жу; ТыЕч2лБв=жешь; ТыЕч3лБв=жет;//будущее на ты ВыЕч1лБв=жу; ВыЕч2лБв=жете; ВыЕч3лБв=жет;//будущее на вы МыЕч1лБв=жем; МыЕч2лБв=жете; МыЕч3лБв=жут;//будущее на мы Мч1лБв=жем; Мч2лБв=жете; Мч3лБв=жут;" //будущее мн.число pat="скажи $L1" } персонаж.сказал(L1) { if(told) { if(свобода_воли(L1)==нет) return //персонаж отказался выполнить действие, или выполнил его по-своему } var Error = this > L1+"." //говорим if(Error) %<font color=gray>({Error})</font> }
Чтобы рассмотреть всё это сразу, рассмотрим пример:
> скажи "крот, дай очки"
- команда, введённая игроком. Обрабатывается как действие (action). Срабатывает действие "сказал" для персонажа pers, выполняющая this > L1+".", которая произносит фразу от лица this.
Мышонок > крот, дай очки
- фраза, сказанная персонажем. Обрабатывается как фраза (phrase). Срабатывает фраза "команда" для крота, выполняющая this < L1 + " " + L2
Оператор "<" даёт команду персонажу. У нас это "крот < дай очки" (на экране не отображается). Срабатывает действие "дал" для крота с параметром Что=очки.
крот дал очки мышонку.
Вот таким образом можно приказывать несобственным персонажам, если они захотят слушаться, конечно
// ==================================================================== // стандартные сообщения парсера об ошибках // ==================================================================== //это предложение непонятно! global.is_not_recognized = "это мне непонятно^^^" //два числа подряд недопустимы! global.two_num_or_more = "сколько, сколько?" //число в конце команды недопустимо! global.number_at_end = "чего {number}?" //слово '{word}' мне неизвестно. global.unknown_word = "я не знаю что такое {word}!" //слово '{word}' не подошло ни к одному объекту. //global.unusable_word = //'{word}' - слово в неверной форме. //global.incorrect_form = //{object} отсутствует. global.object_not_found = "{здесь_или_у_тебя(object.можно_взять)} нет {очистить_лексему(object).lex*Рп}!" здесь_или_у_тебя(Flag) { if(Flag) return "у {actor*РпНе}" else return "здесь" } //{object} не годится для этого действия. global.unusable_object = "{actor} не {может*actor} использовать {очистить_лексему(object).lex*Вп} подобным образом!" //слишком сложное предложение. global.too_difficult_statement = "это слишком сложно для моего понимания!" //не удалось подобрать объект к слову '{word}'. //global.err_situation = //Непонятно что имелось в виду: //global.object_select = //это невозможно сделать! global.can_not_execute = "{actor} не {может*actor} {act.инфинитив}!" //никто не ответил. //global.no_reply =
На вики довольно хорошо описаны сообщения парсера. Не буду повторяться, а процитирую часть описания от туда:
"Сообщения парсера хранятся в предопределенных глобальных переменных. ТОМ инициализирует их при старте, затем они могут быть переопределены в любое время. Более того, свойства с этими же именами могут быть прописаны для актера, для действия или фразы, а также для объекта. При возникновении ошибки парсер ищет необходимое сообщение для объекта относительно действия, если не найдено - для действия относительно актера, если не найдено - для актера, и затем в объекте global."
Остальное понятно из комментариев в коде.
Вот мы и рассмотрели все операторы и конструкции языка ТОМ, встретившиеся в модуле Main.tml. На подведение итогов и структуризацию увы места уже нет. Придётся вынести это в отдельную часть.
В следующей части мы ещё раз пробежимся по всему модулю Main.tml, но уже с целью вынести все классы, их методы, вывести список действий и фраз, чтобы описать назначение каждого объекта, какие свойства и методы нам нужно менять в унаследованных объектах, а какие трогать не следует. Всё это поможет нам использовать стандартную библиотеку "на полную мощность" и не копаться каждый раз в километрах кода, чтобы вспомнить как называется тот или иной метод.
До встречи!