Большая часть
функциональности игровых DLL определяется функциями, предоставляемыми
движком игры. Чтобы создавать собственные объекты и эффекты, вам
необходимо хорошо в них разбираться. В этой статье я хочу рассмотреть
функции серверной DLL, которые могут быть вам полезны – для чего они
нужны и как им применять. Большая часть
функциональности игровых DLL определяется функциями, предоставляемыми
движком игры. Чтобы создавать собственные объекты и эффекты, вам
необходимо хорошо в них разбираться. В этой статье я хочу рассмотреть
функции серверной DLL, которые могут быть вам полезны – для чего они
нужны и как им применять.
Все функции движка находятся в структуре enginefuncs_t, которая
передается в DLL в функции GiveFnptrsToDll ("Give function pointers to
DLL”). Она вызывается движком сразу после инициализации библиотеки и
заполняет локальную копию структуры – переменную g_engfuncs. Вы можете
вызывать эти функции двумя способами:
1) Прямой вызов по указателю, например: g_engfuncs. pfnMessageEnd();
2) Вызов при помощи макроса или инлайн-функции, предопределенных в файле enginecallback.h, например: MESSAGE_END();
Последний способ предпочтительнее, т.к. позволяет более гибко передавать
параметры, например, использовать параметры по умолчанию. Впрочем, если
вы в этом пока не очень хорошо разбираетесь, просто используйте второй
способ. Я же буду приводить синтаксис вызова для обоих вариантов –
название для первого и псевдоним для второго.
Хотелось бы сразу устранить некоторую путаницу со строками. В серверной
части игры часто строки хранятся как целочисленные смещения (string_t), а
функции движка работают только с массивами символов (char*). Поэтому
string_t нужно преобразовать к char* вот таким способом:
Таким образом, если вы получаете ошибку компиляции вида «cannot convert
parameter 1 from 'int' to 'char *'», вам нужно сделать это
преобразование.
Вы привыкли работать с классами, а функции движка понимают только
edict_t* в качестве ссылки на энтити. Тут преобразование просто:
1) Из указателя на класс: edict_t *pEdict = pMyClass->edict();
2) Из pev класса: edict_t *pEdict = ENT(pev);
Некоторые функции записывают свои значения по указателям, переданным в
качестве параметров. Например, векторы записываются в указатель на
массив float. Вам нужно самим проследить, что вы передали правильные
параметры! Например, если функция заполняет вектор, то выглядеть вызов
будет так:
Название: pfnPrecacheModel
Псевдоним: PRECACHE_MODEL
Синтаксис: int PRECACHE_MODEL(char* s);
Эта функция загружает модель или спрайт в память и возвращает его
индекс. Если модель не найдена, сервер отключит вас с ошибкой. Параметр s
– имя модели или спрайта, например, "models/player.mdl”. Прежде чем
использовать модель или спрайт в игре, их необходимо прекэшировать. Эта
функция может быть вызвана только в функции Spawn класса (хотя ее обычно
выносят в отдельную функцию Precache, чтобы ресурсы кэшировалась также
после загрузки сохраненной игры, когда Spawn не вызывается).
Название: pfnPrecacheSound
Псевдоним: PRECACHE_SOUND
Синтаксис: int PRECACHE_SOUND(char* s);
Эта функция загружает звук в память (производя при необходимости
конвертацию в поддерживаемый формат, если это возможно) и возвращает его
индекс. Если звук не найдет, сервер создаст пустой звук. Параметр s –
имя звукового файла, например, "common/null.wav”. Прежде чем
использовать звук в игре, его необходимо прекэшировать. Ограничения на
вызов те же, что и у pfnPrecacheModel (см. выше).
Название: pfnPrecacheGeneric
Псевдоним: PRECACHE_GENERIC
Синтаксис: int PRECACHE_GENERIC(char* s);
Эта функция проверяет наличие произвольного ресурса, загружает его и
возвращает его индекс. Зачем этот индекс может понадобиться, знают
только разработчики... А польза от функции в том, что при сетевой игре,
если файл отсутствует у клиента, он будет ему закачан с сервера, и
клиентская DLL сможет получить к нему доступ. Ограничения на вызов те
же, что и у pfnPrecacheModel (см. выше).
Название: pfnSetModel
Псевдоним: SET_MODEL
Синтаксис: void SET_MODEL(edict_t *e, const char *m);
Устанавливает модель или спрайт для энтити. Ресурс должен быть
прекэширован (при помощи PRECACHE_MODEL). Первый параметр – энтити (как
правило, текущая, так что можете написать ENT(pev), второй – название
модели (такое же, как в PRECACHE_MODEL, только для преобразования из
string_t достаточно просто STRING, без const_cast’а).
Название: pfnModelIndex
Псевдоним: MODEL_INDEX
Синтаксис: int MODEL_INDEX(const char *m);
Возвращает индекс модели или спрайта (тот же, что и функция PRECACHE_MODEL). Параметр – имя модели (см. pfnSetModel).
Название: pfnModelFrames
Псевдоним: MODEL_FRAMES
Синтаксис: int MODEL_FRAMES(int modelIndex);
Функция работает со спрайтами и возвращает количество кадров анимации в
них. Параметр – индекс спрайта, возвращаемый функциями PRECACHE_MODEL
или MODEL_INDEX. Если была вызвана SET_MODEL, то для текущего спрайта вы
также можете использовать pev->modelindex.
Название: pfnSetSize
Псевдоним: SET_SIZE
Синтаксис: void SET_SIZE(edict_t *e, const float *rgflMin, const float *rgflMax);
Устанавливает размер Bounding Box энтити (ее размер для проверки
столкновений и пересечений). Первый параметр – минимумы по X,Y и Z,
второй – максимумы; они задаются в относительных координатах. Удобнее,
однако, использовать функцию UTIL_SetSize:
Пример вызова:
UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8));
Так задается размер 16х16х16 для текущей энтити. Ее центр окажется в
центре коробки (центр – это точка (0, 0, 0) в относительных
координатах).
Название: pfnChangeLevel
Псевдоним: CHANGE_LEVEL
Синтаксис: void CHANGE_LEVEL(char* s1, char* s2);
Осуществляет смену уровня (см. trigger_changelevel). Первый параметр –
имя нового уровня, второй – имя ландмарка (info_landmark). Тут есть одна
тонкость – чтобы правильно вычислить относительные координаты игрока по
отношению к ландмарку нового уровня (абсолютные-то другие!), нужно
сохранить положение ландмарка на текущем уровне в переменную
gpGlobals->vecLandmarkOffset.
Название: pfnGetSpawnParms
Псевдоним: GET_SPAWN_PARMS
Синтаксис: void GET_SPAWN_PARMS(edict_t *ent);
О назначении этой функции можно лишь догадываться. В Quake существовал
набор 16 уникальных значений, закрепленных за клиентом – spawn_parms.
Они хранились в структуре движка, но могли передаваться в progs.dat для
обработки. В Half-Life это, похоже, было упразднено. Таким образом,
данная функция не используется.
Название: pfnSaveSpawnParms
Псевдоним: SET_SPAWN_PARMS
Синтаксис: void SET_SPAWN_PARMS(edict_t *ent);
Как и pfnGetSpawnParms, эта функция не используется в Half-Life.
Название: pfnVecToYaw
Псевдоним: VEC_TO_YAW
Синтаксис: float VEC_TO_YAW(const float *rgflVector);
Возвращает yaw-компонент угла (поворот относительно оси oZ), в который
конвертируется вектор rgflVector. Сам вектор – обычно разность конечной и
начальной точек. Удобнее использовать функцию UTIL_VecToYaw:
C++ Source Code:
float UTIL_VecToYaw( const Vector &vec );
Пример: вы хотите сделать голограмму, которая всегда повернута к игроку.
Допустим, у вас есть указатель на класс игрока и функция
CMyHologram::Think, вызываемая каждые 0.1 с. Тогда код будет выглядеть
так:
Название: pfnVecToAngles
Псевдоним: VEC_TO_ANGLES
Синтаксис: void VEC_TO_ANGLES(const float *rgflVectorIn, float *rgflVectorOut);
Более общая функция, чем pfnVecToYaw: она также вычисляет pitch и
roll-компоненты угла, иными словами, полное значение угла. Сам вектор –
обычно разность конечной и начальной точек. Удобнее использовать функцию
UTIL_VecToAngles:
C++ Source Code:
Vector UTIL_VecToAngles( const Vector &vec );
В вышеупомянутом примере можно заставить модель вести себя подобно спрайту, то есть поворачиваться к игроку всегда «лицом»:
Название: pfnMoveToOrigin
Псевдоним: MOVE_TO_ORIGIN
Синтаксис: void MOVE_TO_ORIGIN(edict_t *ent, const float *pflGoal, float dist, int iMoveType);
Эта функция заставляет монстров ходить и летать. Первый параметр – edict
монстра, второй – массив из трех значений с плавающей точкой –
координаты точки, куда надо переместиться. Третий – размер «шага»
монстра, т.е. на сколько его нужно переместить в заданном направлении.
Обычно для наземных монстров это значение не превышает 16, иначе будут
проблемы со ступеньками лестниц. Последний параметр – тип перемещения.
Он может быть равен MOVE_NORMAL (монстр идет туда, куда смотрит, при
необходимости поворачиваясь – характерно для наземных) или MOVE_STRAFE
(монстр не поворачивается – характерно для летающих). Удобнее
использовать функцию UTIL_MoveToOrigin:
Название: pfnChangeYaw
Псевдоним: -
Синтаксис: void g_engfuncs.pfnChangeYaw (edict_t* ent);
Эта функция изменяет yaw-компонент угла энтити следующим образом:
приближает значение pev->angles.y к pev->ideal_yaw со скоростью
pev->yaw_speed и приводит угол в диапазон 0..360. Ничего особо
оригинального, в Half-Life это делается в функции
CBaseMonster::ChangeYaw. Эта функция досталась нам в наследство от
Quake, где был интерпретируемый код и все часто вызываемые функции
старались сделать частью движка, а не progs.dat.
Название: pfnChangePitch
Псевдоним: CHANGE_PITCH
Синтаксис: void CHANGE_PITCH(edict_t* ent);
Аналогично pfnChangeYaw, функция изменяет pitch-компонент угла энтити
следующим образом: приближает значение pev->angles.x к
pev->idealpitch со скоростью pev-> pitch_speed и приводит угол в
диапазон 0..360. В Half-Life эта функция, как и значения
pev->idealpitch и pev->pitch_speed, не используется.
Название: pfnGetEntityIllum
Псевдоним: GETENTITYILLUM
Синтаксис: int GETENTITYILLUM(edict_t* pEnt);
Очень интересная функция – возвращает освещенность данной энтити.
Освещенность берется из лайтмапы под энтитей или над энтитей (если
указан эффект EF_INVLIGHT). Значение лежит в интервале от 0 (полная
темнота) до 255 (абсолютно яркая лайтмапа). Это может не работать для
энтитей с невидимыми моделями (например, для игрока всегда возвращается
значение 0). Если вы хотите определять освещенность игрока, вам нужно
будет сделать энтитю, которая будет «привязана» к нему (постоянно
перемещаться с игроком) и брать ее уровень освещенности (в качестве
модели удобно использовать пустышку – модель без полигонов). В классе
CBaseEntity есть перегружаемая функция Illumination, и лучше
использовать ее (например, для игрока к этому значению прибавляется
яркость вспышки, если игрок стреляет).
Название: pfnSetClientMaxspeed
Псевдоним: -
Синтаксис: void g_engfuncs.pfnSetClientMaxspeed(const edict_t *pEdict, float fNewMaxspeed);
Эта функция позволяет установить максимальную скорость для одного
конкретного игрока. Первый параметр – его edict, второй – значение
максимальной скорости. Если указать 0, то будет установлено значение по
умолчанию (задаваемое переменными sv_maxspeed и/или cl_forwardspeed).
Название: pfnGetGameDir
Псевдоним: GET_GAME_DIR
Синтаксис: void GET_GAME_DIR(char *szGetGameDir);
Эта функция служит для получения игровой директории мода. Параметр –
адрес массива символов, в который запишется путь (убедитесь, что он
достаточного размера). Пример: