Добавлен , опубликован

Основы программирования Корсаров

Содержание:
Директивы — это специальные команды, которые начинаются с символа # и НЕ заканчиваются точкой с запятой. Есть несколько типов директив, которые мы рассмотрим ниже.

Директива #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.

Директива #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 (если НЕ определено).
Таким образом, мы можем разрешать или запрещать выполнять какой-то код, если было определено какое-то значение.
Заглянем в файл 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"
Данные директивы отвечают за обработку событий и подключение библиотек.
Это тема для более глубокого изучения взаимодействия скриптовой части игры с движком и, в рамках курса основ, она рассматриваться не будет.

`
ОЖИДАНИЕ РЕКЛАМЫ...
29
Когда препроцессор встречает макросы-объекты с текст_замена
Почему препроцессор, если этим занимается лексер?
Ответы (4)
22
nazarpunk, потому что "директивы препроцессора".
Чтоб читателя не нагружать лишней информацией.
А занимается этим все тот же внутренний "компайлер" в движке.
29
avuremybe, ну тогда вообще непонятно, почему define не сработает внутри строкового литерала.
22
nazarpunk, не проверял, кстати. Но я не думаю, что там такие примитивные ошибки есть.
Я сильно в ихний "компайлер" не вникал, пока что. Только поверхностно смотрел.
Хочешь, я тебе линкану. Может тебя заинтересует))
29
Я сильно в ихний "компайлер" не вникал, пока что. Только поверхностно смотрел.
Там скорее всего оргызок от плюсов. И построен он на старом добром "лексер, прасер, визитор". И если лексер не распознает идентификатор из таблицы define то магии не случится. По этой же причине, кстати, не работают комментарии внутри строковых литералов.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.