Итак, что такое плейсхолдеры? Будем понимать под ними некоторые зарезервированные слова в сообщениях, которые в момент вывода на экран будут заменятся на нечто иное, более соответствующее контексту. Т.е. чтобы получить «Hello, Narg! How are you?» вместо такого кода
Code:
display_msg( message_str( NAME, 100) + obj_name(dude_obj) + message_str( NAME, 101));
{100}{}{Hello, }
{101}{}{! How are you?}
{100}{}{Hello, }
{101}{}{! How are you?}
Хотелось бы писать нечто подобное:
Code:
Display_msg( parse(message_str(NAME,100)));
{100}{}{Привет, %dude_name%! Как дела?}
{100}{}{Привет, %dude_name%! Как дела?}
Этот магический %dude_name% и есть плейсхолдером, который заменяется на реальное имя ГГ. Самый большой интерес представляет функция parse, которая заменяет текст с плейсхолдерами на реальное сообщение.
Code:
// Здесь последовательно находятся все строки заключенные в % и %, и заменяются на их представление.
procedure parse( variable str )
begin
variable token, rest, line, result;
line := tokenize(str, 0, '%');
result := line;
while line != str do
begin
token := tokenize(str, line, '%');
line += "%" + token;
if token == “”
result += “%”;
else if token == “dude_name” then
result += obj_name(dude_obj);
rest := tokenize(str, line, '%');
line += "%" + rest;
result += rest;
end
return result;
end
procedure parse( variable str )
begin
variable token, rest, line, result;
line := tokenize(str, 0, '%');
result := line;
while line != str do
begin
token := tokenize(str, line, '%');
line += "%" + token;
if token == “”
result += “%”;
else if token == “dude_name” then
result += obj_name(dude_obj);
rest := tokenize(str, line, '%');
line += "%" + rest;
result += rest;
end
return result;
end
Важным моментом здесь является выбор разделителя, для обозначения тела плейсхолдера. В данном случае я выбрал знак ‘%’. Это обозначает, что все строки, заключенные в пару разделителей будут распознаваться как плейсхолдеры, также это обозначает, что в теле плейсхолдера использование % недопустимо. Также сам символ % можно получить путем записи пары разделителей ( «95%%» будет интерпретировано как «95%» ), но все же такой вид нельзя применять внутри тела плейсхолдера.
Такая реализация несколько ограниченна, так как для каждого вида плейсхолдера нужен свой блок if-then, и при достаточном их количестве код функции распухнет до неприличных размеров.
Выходом может быть применение функций. Замечательной особенностью SSL есть то, что функции могут вызываться не только через идентификатор ( call foo(); ), но и с использованием строкового представлении имени функции ( call “foo”(); ). Имея такую возможность функцию parse можно изменить таким образом:
Code:
//-----------------
line += "%" + token;
if token == “”
result += “%”;
//else if token == “dude_name” then
// result += obj_name(dude_obj); это убираем
else
result += token(); // а это добавляем
rest := tokenize(str, line, '%');
//-----------------
line += "%" + token;
if token == “”
result += “%”;
//else if token == “dude_name” then
// result += obj_name(dude_obj); это убираем
else
result += token(); // а это добавляем
rest := tokenize(str, line, '%');
//-----------------
Теперь нужно только определить функции для всех плейсхолдеров. Например так:
Code:
procedure dude_name()
begin
return obj_name( dude_obj );
end
procedure dude_money()
begin
return item_caps_total( dude_obj );
end
begin
return obj_name( dude_obj );
end
procedure dude_money()
begin
return item_caps_total( dude_obj );
end
Теперь запросто можно написать:
Code:
display_msg( parse( “Ah, %dude_name%, only %dude_money% caps? Hm, not so much.” ));
Пока все прекрасно работает, и может успешно применятся. Но заканчивать на этом как-то не интересно. Вышеописанная возможность SSL вызывать функции с помощью строкового представления их имен дает огромные возможности, граничащие с возможностью писать скрипты в MSG-файлах. Но пока не станем настолько радикально использовать данный подход, а лишь рассмотрим некоторое расширение.
Итак, поставим такую задачу: выводить для каждого криттера, к которому привязан данный скрипт, при наведении на него курсора, выводить «Это Наркоман, у которого 15 монет.»
Исходное сообщение может выглядеть так:
Code:
{100}{}{Это %name%, у которого %money% монет.}
Здесь присутствую два плейсхолдера, которые нетрудно определить, их реализацию я оставлю на самостоятельное рассмотрение.
Пока ничего нового, но что делать, если криттер женского пола? Нам нужно в зависимости от пола использовать либо «которого» либо «которой». Для этого можно применить более сложный плейсхолдер, который имеет аргументы, например так:
Code:
{100}{}{Это %name%, у котор%sex:ого|ой% %money% монет.}
Здесь я применяю плейсхолдер с аргументами %sex:ого|ой%, который в зависимости от пола объекта, к которому привязан данный скрипт заменяется либо на «ого» либо на «ой». Для обработки плейсхолдеров такого расширенного вида придется немного поменять функцию parse. необходимо заменить вызов
Code:
result += token();
на
Code:
result += placeholder( token );
и определить функцию:
Code:
// Вызывает функцию обработчик с аргументами или без. Имя от аргументов отделяются ‘:’.
procedure placeholder( variable name )
begin
variable name1, args;
name1 := tokenize( name, 0, ':'); // получаем имя плейсхолдера
if name1 != name then // если присутствуют аргументы
begin
args := tokenize( name, name1, ':' );
return name1(args); // отделяем имя от аргументов и вызываем функцию с аргументами
end
return name1(); // иначе вызываем функцию без аргументов
end
procedure placeholder( variable name )
begin
variable name1, args;
name1 := tokenize( name, 0, ':'); // получаем имя плейсхолдера
if name1 != name then // если присутствуют аргументы
begin
args := tokenize( name, name1, ':' );
return name1(args); // отделяем имя от аргументов и вызываем функцию с аргументами
end
return name1(); // иначе вызываем функцию без аргументов
end
Теперь необходимо определить функцию-обработчик:
Code:
// Возвращает первый аргумент, если объект мужского пола, иначе – второй аргумент. Аргументы разделены ‘|’
procedure sex( variable args )
begin
variable m,f;
m := tokenize( args, 0, '|' ); // мужской
f := tokenize( args, m, '|' ); // женский
if get_critter_stat( self_obj, 34 ) == 1 then
return f;
else
return m;
end
procedure sex( variable args )
begin
variable m,f;
m := tokenize( args, 0, '|' ); // мужской
f := tokenize( args, m, '|' ); // женский
if get_critter_stat( self_obj, 34 ) == 1 then
return f;
else
return m;
end
Вот таким несложным образом можно продуцировать необходимые окончания, или целые предложения. Как видно из примера аргументы в функцию-обработчик передаются одной строкой, и то, как вы будете их интерпретировать – всецело ваше дело. Важно лишь отделять имя плейсхолдера от аргументов единообразным образом, например с помощью ‘:’.
На этом возможности плейсхолдеров не оканчиваются. Вообще они ограничены лишь вашим воображением, но могу предложить еще одно интересное применение. При использовании вот такого плейсхолдера можно примерно определять интеллект криттера:
Code:
{100}{}{Это %name%, у котор%sex:ого|ой% %iq:123|низкий|456|средний|78910|высокий|% интеллект.}
Плейсхолдер iq, в зависимости от значения интеллекта криттера будет заменяться на соответствующее текстовое представление. Определить функцию-обработчик можно, например, так:
Code:
procedure iq(variable args )
begin
variable str, iqq;
iqq := get_critter_stat( self_obj, 4 ) + "";
str := tokenize( args, iqq, '|');
return str;
end
begin
variable str, iqq;
iqq := get_critter_stat( self_obj, 4 ) + "";
str := tokenize( args, iqq, '|');
return str;
end
Вот так вот с помощью нескольких строчек кода можно реализовать достаточно мощное и гибкое средство плейсхолдеров. На самом деле такая реализация имеет один ОЧЕНЬ большой недостаток. Каждый скрипт, использующий плейсхолдеры должен содержать в себе определения ВСЕХ функций-обработчиков, т.к. заранее неизвестно, какие из них будут применятся в файле сообщений. При достаточном количестве этих обработчиков размер скрипта может вырасти до космических масштабов, а скрипт такой далеко не один. Поэтому удобнее иметь под рукой библиотеку таких функций, которые буду экспортироваться во все скрипты, при этом код будет присутствовать только в одном скрипте. Но это совсем другая история, о которой я расскажу позже.