Официальная документация GMDK: Скриптовый язык Daedalus

Материал из Gothic Library Wiki
Перейти к навигацииПерейти к поиску

Введение[править | править код]

Язык сценариев, разработанный нами для игры, упоминается в следующей статье - 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(); };