Digs - Персональная территория

Авторский проект Артема Глазкова
? 
        Версия для печати (цвет)  





Написание сценариев на QuakeC

    Начнём с понятий. Перво-наперво следует представлять границу между движком и сценарием, заодно и поймём, что каждый из них из себя представляет.

    Сразу понятно, что движок представлен EXE-файлом. В его задачу входит обработка графики, физики, а также интерфейса пользователя (отрисовка меню, отрисовка консоли и т.д.). При этом под физикой понимается взаимодействие некоего объекта с окружающим миром и другими объектами. На его уровне (движка) нет таких понятий как монстр, дверь, кнопка, секрет и т.д. Движок оперирует только объектами. Он представляет как бы абстрактное начало для всего остального.

    На сценарии лежит ответственность за описание конкретных объектов, их поведение в зависимости от ситуации. Сценарий для игры пишется на языке, который называется QuakeC. В качестве выходного кода получаем файл progs.dat. Здесь можно скачать исходники и компилятор сценария для версии 1.01. Нет ничего страшного в том, что у Вас движок большей версии (например 1.06). Это не имеет большого значения.

    Разберемся, как пользоваться этими текстами. Программа qccdos.exe компилирует исходные тексты и при нормальной компиляции создает файл progs.dat. Загляните в файл progs.src. Здесь первой строкой указывается, куда будет произведена компиляция. Далее указывается, какие файлы будут включены в проект. Обычно делается так. В каталоге Quake заводим произвольный подкаталог. Например, назовем его proba. В нем заводим подкаталог, например source и распаковываем туда все файлы из архива. В файле progs.src первой строкой ставим ../progs.dat. Запускаем компилятор. В каталоге proba создался файл progs.dat. Теперь, для того чтобы играть именно с этим сценарием, запускаем игру следующим образом:

quake -game proba

    Теперь немного о самих текстах. Язык является C-подобным (следует из название). Полную спецификацию можно прочитать здесь. Хотите, сами изучайте. Опишу только несколько деталей (функций), чтобы было от чего оттолкнуться.



    Открываем в редакторе файл client.qc и находим функцию SetNewParms. Данная функция устанавливает количество снаряжения, оружие, защиту и др. и вызывается всегда, когда начинаем новую игру. Рассмотрим что есть в параметрах:

parm1Определяет, что в данный момент "навешано" на игрока. Все что сюда может быть включено, начинается префиксом IT_. Список слов с данным префиксом можно найти в файле defs.qc. Все элементы должны быть скреплены символом "|" ("ИЛИ"). Например: parm1 = IT_SHOTGUN | IT_AXE | IT_ROCKETLAUNCHER | IT_ARMOR1;
parm2Количество энергии
parm3Количество защиты
parm4Количество патронов
parm5Количество гвоздей
parm6Количество ракет
parm7Количество электроэлементов
parm8Текущее оружие. Подставляется константой. Например: parm8 = IT_ROCKETLAUNCHER;
parm9 Коэффициент защиты. Для зеленой:parm9 = 35;. Для желтой: parm9 = 60;. Для красной: parm9 = 80;

    Для чего это можно использовать? Например можно записывать демонстрашку не только для первого уровня, но и для любого. Доходим до нужного уровня и записываем все характеристики игрока на бумажечку. Затем переносим все это в исходник и стартуем. У нас даже на "Интро" будет нужное количество оружия. Запускаем запись демки командой record.



    Следующий пример касается создания своих консольных команд. На самом деле, насколько мне известно, свою команду напрямую создать нельзя. Но можно обработать команду impulse со своим параметром (число, которое не используется стандартом). После этого просто создаем алиас: alias mycommand "impulse my_code".

    Открываем файл weapons.qc. Находим в нем функцию ImpulseCommands. Дописываем следующий код:

  if (self.impulse == 100){
        
self.super_time 1;
        
self.super_damage_finished time 3000;
        
self.items self.items IT_QUAD;
  }

    Теперь компилируем. Пока не запустили игру, добавляем в autoexec.cfg строку:
  alias superquad "impulse 100"

    А далее все просто. Запускаем Quake, выходим на нужный уровень, вводим с консоли команду superquad и у нас включается QuadDamage-режим на 3000 секунд!



    Далее попробуем решить маленькую проблемку. Не знаю как у других, а в моем варианте Quake допущена ошибка (в сценарии): монстр Fish регистрируется дважды. Соответственно, если, например, на уровне 25 монстров, из них 5 - это монстры класса Fish, то, нажав tab, Вы увидите, что у Вас 30 монстров. Я первое время понять не мог, бегаю по уровню, всех убил, а где-то пять еще есть. Давайте решим данную проблему. Открываем файл monsters.qc. Находим там функцию swimmonster_start_go. Она вызывается из функции swimmonster_start. Последняя функция вызывается для установки параметров и регистрации в уровне плавающего монстра. Находим такую строку:

  total_monsters total_monsters 1;

    Данная операция увеличивает количество монстров на уровне. Теперь если мы посмотрим на текст функции swimmonster_start_go, то увидим там тоже такую же строку. Получается, что монстр регистрируется дважды. Просто-напросто удаляем одну из этих строк. Ну а далее компилируем и с этого момента все будет нормально.



   Теперь поговорим о том, как инициализируются различные объекты. Когда загружается уровень, в нем присутствуют описания объектов приблизительно в следующем виде:

{
"classname" "func_door"
"speed" "100"
"sounds" "1"
"wait" "3"
"lip" "8"
"angle" "180"
"model" "*3"
}

    Как же создается объект? Все объекты, как я понимаю, имеют единую структуру, в которой меняются значения полей, а также адреса функций обрабатывающих различные события. При чтении такой структуры движок создает пустой объект с именем Self и помещает в него свойства из этой структуры. Далее считывается свойство "classname" и в файле сценария вызывается функция с соответствующим именем. Данная функция анализирует свойства структуры и исходя из этого может сделать подгрузку файлов (например, звуковых или моделей), а также заполнить другие свойства и установить функции-обработчики для различных событий.

    Для того чтобы создавать свои элементы не обязательно их делать с нуля. Можно просто "перегрузить" уже готовый элемент. Рассмотрим, для примера, функцию

void() trigger_once =
{
        
self.wait = -1;
        
trigger_multiple();
};


    В данном случае видно что триггер once, то же самое что и multiple за исключением свойства wait, которое заставляет триггер сработать только один раз, а затем "уйти" в бесконечное ожидание.

    Самый простой способ переопределения стандартных объектов, это добавление новых свойств. Это позволит сразу вставлять новый объект в редакторе уровней как старый, а затем (по крайней мере Qoole это позволяет) добавить новое свойство и присвоить ему нужное значение. При этом в сценарии, в родителе нашего объекта, проверять наличие этого нового свойства и при его обнаружении считать, что это уже другой объект.

    Другой способ (тоже простой) - это расширить некоторое свойство объекта. К примеру, класс trigger_once имеет свойство sounds. Это звук, который выдается при активации триггера. Всего их там три. Мы можем переписать исходники таким образом, чтобы появился еще один звук, например с индексом 4, а затем непосредственно в объекте указать код звука (не пользуясь выпадающим списком). Лично я, таким образом, умудрялся добавлять в игру звук грома.

© 2005-16, Powered By Digs (Написать письмо, vk)