Директивы — это специальные команды, которые начинаются с символа # и НЕ заканчиваются точкой с запятой. Есть несколько типов директив, которые мы рассмотрим ниже.
Директива #include
В начале почти любого файла скрипта в Корсарах присутствует директива #include. Она подключает другой файл с кодом, к тому файлу, в котором была задействована.
Например, файл globals.c начинается с таких строк:
#include "sound.h"
#include "messages.h"
#include "sound.c"
//--------------------------------------------------------------------
// Math section
При выполнении этого кода, препроцессор под строкой с директивой #include скопирует весь код из указанного файла.
То есть после обработки это будет выглядеть вот так:
То есть после обработки это будет выглядеть вот так:
// наша директива include
#include "sound.h"
// копирует весь код из файла sound.h
#ifndef _PROGRAM_SOUND_H_
#define _PROGRAM_SOUND_H_
#define SOUND_INVALID_ID 0
// SoundPlay (..., type, ...)
#define SOUND_WAV_3D 1
#define SOUND_MP3_STEREO 2
#define SOUND_MP3_3D 3
#define SOUND_WAV_STEREO 4
// SoundPlay (..., volume_type, ...)
#define VOLUME_FX 1
#define VOLUME_MUSIC 2
#define VOLUME_SPEECH 3
//SoundSet3DParam(..., type, ...)
#define SOUND_PARAM_MAX_DISTANCE 1
#define SOUND_PARAM_MIN_DISTANCE 2
#define SOUND_PARAM_POSITION 3
#endif
// а далее идет остальной код в файле
#include "messages.h"
#include "sound.c"
//--------------------------------------------------------------------
// Math section
Это позволяет разделить большой проект на небольшие логические сегменты, не создавая мешанину из кода. Представьте, чтобы весь код игры лежал в одном файле. Как там можно что-то разобрать или найти? А так в каждом файле лежит код, который отвечает за свой участок работы. А в нужном месте просто подключается другой файл с необходимыми функциями внутри.
Необходимо помнить, что выполнение скриптов начинается с файла ../PROGRAM/seadogs.c, а все остальные файлы, так или иначе, подключаются к нему. Это значит, что адреса подлючаемых директивой #include файлов нужно указывать относительно расположения seadogs.c.
Например, вы хотите в файл ../PROGRAM/Encounters/encounters.c подключить файл encounters.h, который находится в этой же папке. Не смотря на то, что файл находится рядом с тем файлом, в который вы его подключаете, нужно писать #include "encounters\encounters.h". Потому что с точки зрения кода, вы подключаете его в файл seadogs.c, который находится в папке ../PROGRAM.
Например, вы хотите в файл ../PROGRAM/Encounters/encounters.c подключить файл encounters.h, который находится в этой же папке. Не смотря на то, что файл находится рядом с тем файлом, в который вы его подключаете, нужно писать #include "encounters\encounters.h". Потому что с точки зрения кода, вы подключаете его в файл seadogs.c, который находится в папке ../PROGRAM.
Директива #define
Директива #define используется для создания макросов. Макрос — это правило, которое определяет конвертацию идентификатора в указанные данные.
Есть два основных типа макросов: макросы-функции и макросы-объекты.
Макросы-функции ведут себя как функции и используются в тех же целях. Мы не будем их обсуждать, так как их использование считается опасным, и почти всё, что они могут сделать, можно осуществить с помощью простой (линейной) функции.
Макросы-объекты можно определить одним из следующих двух способов:
#define идентификатор
#define идентификатор текст_замена
Когда препроцессор встречает макросы-объекты с текст_замена, то любое дальнейшее появление идентификатор заменяется на текст_замена. идентификатор обычно пишется заглавными буквами с символами подчёркивания вместо пробелов.
В скриптовой части Корсаров такие макросы используются вместо константных переменных, так как объявляются один раз и не могут быть изменены в дальнейшем.
В скриптовой части Корсаров такие макросы используются вместо константных переменных, так как объявляются один раз и не могут быть изменены в дальнейшем.
Если мы снова посмотрим на тот же файл globals.c, то после подключения внешних файлов как раз происходит объявление типичных математических констант при помощи директивы #define:
//--------------------------------------------------------------------
// Math section
//--------------------------------------------------------------------
#define PI 3.14159265
#define PIm2 6.28318530
#define PId2 1.57079632
Таким же образом можно подставлять и текстовые значения, только не забывайте брать их в кавычки, как все строки.
Выше мы сказали, что макросы-объекты могут быть объявлены без текст_замена:
#define COMMON_EVENT_HPP_SCRIPT
Как несложно догадаться, встретив в коде COMMON_EVENT_HPP_SCRIPT препроцессор заменит этот идентификатор... Ничем. Пустым местом.
Это может показаться довольно бесполезным, но такие макросы используются для других целей. А именно - для условной компиляции.
Это может показаться довольно бесполезным, но такие макросы используются для других целей. А именно - для условной компиляции.
Условная компиляция
В начале урока мы узнали, что к нашему файлу с кодом могут быть подключены другие файлы, содержимое которых будет скопировано в наш текущий файл. Когда файлов становится много, легко попасть в ситуацию, когда один и тот же файл будет подключен несколько раз в разных файлах. Чтобы этого недопустить существуют директивы условной компиляции:
#ifdef
#ifndef
#endif
Работают они точно по тому же принципу, что и условный оператор if/then, только используются с директивами #define.
Собственно, #ifdef - это сокращение от if defined (если определено), а #ifndef - от if not defined (если НЕ определено).
Собственно, #ifdef - это сокращение от if defined (если определено), а #ifndef - от if not defined (если НЕ определено).
Таким образом, мы можем разрешать или запрещать выполнять какой-то код, если было определено какое-то значение.
Заглянем в файл Events.h:
#ifndef COMMON_EVENT_HPP_SCRIPT
#define COMMON_EVENT_HPP_SCRIPT
#define FORT_DESTROYED "FortDestroyed"
#define ABORDAGE_START_EVENT "AbordageStartEvent"
// cannons
#define GET_CANNON_BY_TYPE_EVENT "GetCannonByTypeEvent"
// nations
#define GET_RELATION_EVENT "GetRelationEvent"
// game
#define NEW_GAME_EVENT "NewGameEvent"
#define GAME_OVER_EVENT "GameOverEvent"
// crosshair
#define TELESCOPE_ACTIVE "TelescopeActive"
#endif
Код между директивами #ifndef и #endif будет выполнен только в том случае, если идентификатор COMMON_EVENT_HPP_SCRIPT небыл объявлен ранее.
В данном конкретном случае директивы условной компиляции используются для избежания повторного объявления констант, но ничего не мешает нам написать что-нибудь такое:
#ifndef CUSTOM_MATH
#define CUSTOM_MATH
#include "Math\customRandom.c"
#endif
Защита от повторного подключения одного файла при помощи директив условной компиляции называется термином «header guards».
Другие директивы
В файлах скриптов вы могли натыкаться и на другие директивы:
#event_handler("evntCheckDlg","CheckNextDialog");
#libriary "b_engine"
Данные директивы отвечают за обработку событий и подключение библиотек.
Это тема для более глубокого изучения взаимодействия скриптовой части игры с движком и, в рамках курса основ, она рассматриваться не будет.
Это тема для более глубокого изучения взаимодействия скриптовой части игры с движком и, в рамках курса основ, она рассматриваться не будет.
Чтоб читателя не нагружать лишней информацией.
Я сильно в ихний "компайлер" не вникал, пока что. Только поверхностно смотрел.