Официальная документация GMDK: Скриптовый язык Daedalus
Введение[править | править код]
Язык сценариев, разработанный нами для игры, упоминается в следующей статье - Daedalus. Игровая программа интерпретирует скрипты, преобразовывая их в код C++.
В этой главе описывается язык сценариев Daedalus. При его создании мы использовали особенности нескольких языков: C, Quake C, а также кое-что из Pascal. Однако, некоторые элементы языка Daedalus сильно отличаются от них.
Область применения языка Daedalus
Диалоги / Вывод информации NPC-AI Загрузка Вывод текста NPC (Прототипы и инстанции) Предметы (Прототипы и инстанции) Исполнение задуманных идей Программирование событий (реализация сложных логических головоломок) Декларирование аудио ресурсов Декларирование графических ресурсов
Описание синтаксиса[править | править код]
Есть 5 видов так называемых маркеров: идентификаторы (identifier), ключевые слова (keywords), литералы (literals), операторы (operators) и другие разделители. Из этих маркеров состоят сценарии. Большие пробелы, разрывы строк, комментарии и т.д. игнорируются. Длина идентификатора не ограничена.
Идентификаторами являются имена переменных, констант, инстанций, прототипов, классов и функций. Идентификатор представляет собой последовательность букв и цифр. Первый символ идентификатора обязательно должен быть буквой. После этого можно использовать цифры от 0 до 9 и символ нижнего подчеркивания.
Ключевые слова:
class
const
else
float
func
if
instance
int
other
prototype
return
self
string
this
var
void
while
Литералами являются строки ("Привет") и постоянные значения (453). Операторы будут рассмотрены позже.
В этом руководстве я описываю скриптовый язык игры, руководствуясь книгой "C++ Programming Language" Бьерна Страуструпа.
Язык скриптов не чувствителен к регистру (в отличии от С).
Мы рекомендуем давать имена идентификаторам следующим образом: например, для файла dungeon.d применять сокращение dng.
Функция: Dng_MoveLift();
Переменная: dng_buttonsPressed;
Константа: DNG_NUM_TRIES;
Комментарии[править | править код]
Комментарии в скриптах обозначаются конструкцией /* Комментарий */. Конструкция // позволяет оставить комментарий, который продолжается до конца строки. Вложенная в строчный комментарий конструкция /* */ или // не будет иметь никакого эффекта. Вложенные комментарии трактуются как простой текст.
Концепция использования комментариев была взята из С++.
Инструкции
Инструкции являются декларациями, состоящими из списка команд или блока операторов:
statement (инструкция)
vardecl (декларация переменных)
constdecl (декларация констант)
assignment (применение)
if-statement (If инструкции)
return-statement (return инструкции)
statement-block (блок инструкций)
Пример:
инструкция: door_open();
инструкция-список: door_open(); opened = TRUE;
блок-инструкция: { door_open(); opened = TRUE; }
Выражения[править | править код]
Операторы[править | править код]
Операторы вычисляют результат одного или двух значений. Оператор сравнения и логические операторы возвращают целое значение 1, если выражение истинно, в противном случае возвращается 0. Бинарные операторы управляют переменными на уровне битов. Приоритет операторов был позаимствован из С++.
Операторы:
calc-op (Вычисляющие операторы)
cmp-op (Операторы сравнения)
bool-op (Булевы операторы)
bit-op (Битовые операторы)
а) Операторы вычисления
calc-op:
+ Сложение
- Вычитание
* Умножение
/ Деление
% Деление с остатком (по модулю)
б) Операторы сравнения
calc-op:
< Меньше
<= Меньше либо равно
> Больше
>= Больше либо равно
== Равно
!= Не равно
б) Булевы операторы
bool-op:
! НЕ
&& И
|| ИЛИ
в) Битовые операторы
bit-op:
& И
| ИЛИ
в) Знак
Знак:
+ Положительный
- Отрицательный
Выражения[править | править код]
Выражения формируются при помощи описанных выше операторов. Ниже приведены примеры выражений.
Выражения:
literal (Литеральные выражения)
calc-expressoin (Выражения вычисления)
cmp-expression (Выражения сравнения)
bool-expression (Булевы выражения)
bit-expression (Битовые выражения)
а)Выражение вычисления
-x1 + x2
x1 * (x2 + x3)
(x2 % 2) * x3
б) Выражение сравнения:
x1 < x2
x1 == x2
в) Булевы выражения:
x1 && x2
x1 || x2
(x0)
Числовые значения являются истинными, если не равняются нулю.
в) Битовые выражения:
x1 | 5;
x1 & 4;
Типы, переменные и константы[править | править код]
Существует два вида типов: Элементарные типы и классы. В отличии от С/С++ другие типы определить нельзя. Классы имеют прямую зависимость от кода самого движка. Переменные величины класса называются инстанциями.
Элементарные типы[править | править код]
float
int
string
Соответствует типам C/C++.
Численные и строковые массивы могут быть образованы следующим образом:
VAR INT attribute[MAX_ATTRIBUTES];
VAR STRING names[MAX_NAMES];
Можно создавать только одномерные массивы. Обращение к элементам массива происходит точно также, как в С++, но при этом константы используются в качестве индекса.
attribute[1] = 0;
Первый элемент массива начинается с нуля.
C++ и Daedalus[править | править код]
Функции, переменные и константы, которые должны быть доступны в C++ и Daedalus, необходимо декларировать, по крайней мере, в Daedalus. Для этого используется ключевое слово extern. Значения переменных и констант дополнительно определяются присваиванием значений.
Переменные и константы
Объявление переменной начинается с ключевого слова var. Это относится ко всем декларациям, а не как в Pascal для всего блока. Однако списки переменных можно объявлять как в С:
Декларация переменной:
var type idetifier[,identifier]opt [...]opt;
Пример:
Верно:
var int wert1, wert2, wert3;
var string frage, antwort;
var int wert;
Неверно:
int value;
Объявление константы должно начинаться с ключевого слова:
Объявление константы:
const type identifier = expression;
Пример:
const type identifier[x] = { expression, expression, expression };
Переменные: Локальные и Глобальные[править | править код]
Область видимости для переменных и констант бывает двух видов:
Переменная или константа, объявленная вне любого блока, является глобальной. Обращение к ней возможно во всей дальнейшей части скрипта.
Переменная или константа, объявленная в блоке, является локальной и остается доступной в пределах этого блока.
Примеры:
var int count;
func void Test()
{ var int x; var int y; }
В данном примере переменная count является глобальной. А переменные x и y, являясь частью блока функции Test(), будут локальными. Точно такой же подход используется в C/C++.
Присваивание.[править | править код]
identifier = expression; // Простое присваивание
Пример:
var int x1;
x1 = 40;
x1 = x1 / 2;
x1 = x1* 3;
Функции[править | править код]
Объявление[править | править код]
Объявление функции начинается с ключевого слова func.
Объявление функции:
func type identifier ( vardecl1opt , ... , vardecl8opt ) statement-block
Пример:
func int UsingSchild(var int x1, var string s1)
{
[...]
};
Передача параметров[править | править код]
Длина списка параметров не ограничена, но желательно использовать наименьшее число параметров для уменьшения количества выделяемой памяти для их хранения. Параметры передаются вызовом по значению. Массивы не допускаются в качестве аргументов.
Вызов функции[править | править код]
Функции вызываются точно так же, как в С++. Вписывается название функции, а также её параметры в скобках.
Классы, шаблоны и инстанции[править | править код]
Классы[править | править код]
Объявление классов точно описывает структуру игрового движка. Поэтому их нельзя расширить, так как они напрямую с ним связаны.
Декларация класса:
class classname (base-classname)opt declaration-block
Пример:
class Creature (Vob)
{
// Атрибуты
var string name;
var int hitpoints;
var int hp_max;
// Действия
var funcref birth;
var funcref death;
};
Атрибутам присваивается значение по умолчанию:
Int переменным присваивается значение 0.
String переменным присваивается значение "".
Ссылочным типам переменных присваивается значение NULL.
Прототипы[править | править код]
С помощью ключевого слова prototype можно создать так называемые прототипы, которые имеют различные значения по умолчанию:
Объявление прототипа:
prototype class-identifier identifier statement-block
Прототип можно рассматривать как "производный" класс, с помощью которого можно изменять все настройки, заданные по умолчанию. Или же, как инстанцию класса, который служит в качестве шаблона для следующих инстанций. Позволяет быстро определить значения, заданные по умолчанию, без постоянного объявления их в блоке.
Существует существенная разница между объявлением класса (class), который отражает точную структуру движка, и определением класса через prototype.
Инстанции[править | править код]
Инстанции классов и прототипы устанавливают свои конкретные представления. Материал является инстанцией класса со всеми специфическими свойствами материала.
Объявление инстанции:
instance class-identifier identifier statement-block
instance instance-identifier prototype-identifier statement-block
В объявлениях блока инстанции используются для определения переменных, которые относятся к определенному классу. При этом неопределенные атрибуты или действия сохраняют свои значения по умолчанию. Также допускаются дополнительные инструкции, если они имеют какой-либо смысл.
Небольшой пример (условный) для элемента Item/Schild/WoodShild
class Item(Vob)
{
// атрибуты
var int damage;
var int attack;
var string description;
// действия
var funcref use;
};
prototype SchildProtoType (Item)
{
damage = 0;
attack = 0;
descriptions = "";
// действия
use = UsingSchild();
};
instance HolzSchild1 (SchildProtoType)
{
// атрибуты
description = "Очень старый деревянный щит";
};
Управляющие конструкции[править | править код]
Условия: if-then-else[править | править код]
Конструкция построения условий взята из C++. Возможность выражений ограничена. Кроме того, всегда ожидается блок объявлений (statement-block).
if-оператор:
if ( expression ) statement-block
if ( expression ) statement-block else statement-block
неправильный: if (x<4) SoundPlay(ID_sound_roar);
правильный: if (x<4) { SoundPlay(ID_sound_roar); };
Возвращаемое значение функции: return[править | править код]
В функциях, которые должны возвращать значения, используется оператор возврата как в C++.
return-выражение:
return ( expression );
Динамические переменные[править | править код]
Некоторые переменные устанавливаются динамически при вызове функции и затем ссылаются на них, как, например, в диалоге между NPC (self) и его собеседником (other). Кроме того, эта встроенная функция позволяет получить доступ к другим VOB.
В поддержку этой концепции, некоторые глобальные переменные объявляются и устанавливаются динамически во время игры, так что они могут быть в любой момент запрошены в функциях. В настоящее время существуют следующие переменные:
VAR C_NPC self;
VAR C_NPC other;
VAR C_NPC victim;
VAR C_NPC hero;
VAR C_NPC item;
Важные отличия от С++[править | править код]
Здесь есть свои подводные камни. Мы их опишем вкратце. Где в С++ разрешаются отдельные инструкции, там в Daedalus должен быть блок инструкций. Это применимо для If-конструкций:
if (x<4) { SoundPlay(); };