Half-Life SDK: Рамка-обводка на выделяемой сущности
Суть: Рисование рамки вокруг сущности (entity), на которую смотрит игрок.
(!) Кого интересует сразу решение рисования — может перейти на шаг 7-ой.
1) В "dlls/player.cpp" получаем ищем нужную сущность. Например в CBasePlayer::UpdateStatusBar(). Сразу после проверки (pEntity->Classify() == CLASS_PLAYER) пишем:
else if (pEntity->Classify() != CLASS_PLAYER) // если сущность НЕ игрок { if (pEntity->IsAlive()) // если сущность жива { MESSAGE_BEGIN(MSG_ONE, gmsgNpcHealth, NULL, this->pev); // gmsgNpcHealth у меня содержит инфу о наблюдаемом NPC вообще WRITE_STRING(STRING(pEntity->pev->classname)); // имя класса сущности (напр. monster_human_grunt) WRITE_SHORT(static_cast<int>(pEntity->pev->health)); // здоровье сущности WRITE_SHORT(static_cast<int>(pEntity->pev->max_health)); // максимальное здоровье сущности WRITE_SHORT(ENTINDEX(pEntity->edict())); // индекс сущности MESSAGE_END(); } else // а это на всякий случай { MESSAGE_BEGIN(MSG_ONE, gmsgNpcHealth, NULL, this->pev); WRITE_STRING(""); WRITE_SHORT(0); WRITE_SHORT(0); MESSAGE_END(); } }
Нам для дела понадобится только "ENTINDEX(pEntity->edict())".
2) В "dlls/UserMessages.h" в самом конце, после остальных сообщений, пишем:
inline int gmsgNpcHealth = 0;
3) В "dlls/UserMessages.cpp" в функции "LinkUserMessages()" пишем:
gmsgNpcHealth = REG_USER_MSG("NpcHealth", -1);
4) В "cl_dll/hud.h" в классе CHudStatusBar (class CHudStatusBar : public CHudBase) пишем (в условный protected):
// Для удобства: в каком порядке отправили из "dlls/player.cpp", в таком и записываем... char NPC_Name[256]; // 1 - classname int NPC_Health; // 2 - health int NPC_MaxHealth; // 3 - maxhealth int NPC_Index; // 4 - индекс сущности
5) В "cl_dll/statusbar.cpp" в начале файла, например после "DECLARE_MESSAGE(m_StatusBar, StatusValue);" декларируем/объявляем наше сообщение:
DECLARE_MESSAGE(m_StatusBar, NpcHealth);
6) В "cl_dll/statusbar.cpp" где-нибудь в конце пишем код ниже. Здесь мы получаем и читаем содержимое, которое мы отправили в "dlls/player.cpp".
bool CHudStatusBar::MsgFunc_NpcHealth(const char* pszName, int iSize, void* pbuf) { BEGIN_READ(pbuf, iSize); strncpy(NPC_Name, READ_STRING(), sizeof(NPC_Name) - 1); // получаем classname NPC_Name[sizeof(NPC_Name) - 1] = '\0'; NPC_Health = READ_SHORT(); // получаем здоровье NPC_MaxHealth = READ_SHORT(); // получаем максимальное здоровье NPC_Index = READ_SHORT(); // получаем индекс m_iFlags |= HUD_ACTIVE; return true; }
7) В "cl_dll/statusbar.cpp" в "Draw()" ("bool CHudStatusBar::Draw(float fTime)") после имеющегося кода пишем:
cl_entity_t* pEntity = gEngfuncs.GetEntityByIndex(NPC_Index); // Ищем сущность с нашим индексом if (pEntity && pEntity->model) { DrawBoundingBox(pEntity); }
где "DrawBoundingBox" это следующая функция:
void CHudStatusBar::DrawBoundingBox(cl_entity_t* pEntity) { Vector vecMins = pEntity->curstate.mins; Vector vecMaxs = pEntity->curstate.maxs; Vector vecOrigin = pEntity->curstate.origin; float flScreenX[8], flScreenY[8]; bool bVisible = false; float minX = 9999, maxX = -9999, minY = 9999, maxY = -9999; for (int i = 0; i < 8; i++) { Vector corner = vecOrigin; corner.x += (i & 1) ? vecMaxs.x : vecMins.x; corner.y += (i & 2) ? vecMaxs.y : vecMins.y; corner.z += (i & 4) ? vecMaxs.z : vecMins.z; float screen[2]; if (gEngfuncs.pTriAPI->WorldToScreen(corner, screen) == 0) { flScreenX[i] = (screen[0] + 1) * gHUD.m_scrinfo.iWidth / 2; flScreenY[i] = (-screen[1] + 1) * gHUD.m_scrinfo.iHeight / 2; minX = min(minX, flScreenX[i]); maxX = max(maxX, flScreenX[i]); minY = min(minY, flScreenY[i]); maxY = max(maxY, flScreenY[i]); bVisible = true; } } if (!bVisible) return; // Рисование рамок int x = static_cast<int>(minX); int y = static_cast<int>(minY); int w = static_cast<int>(maxX - minX); int h = static_cast<int>(maxY - minY); gEngfuncs.pfnFillRGBA(x, y, w, 1, 255, 0, 0, 255); gEngfuncs.pfnFillRGBA(x, y + h, w, 1, 255, 0, 0, 255); gEngfuncs.pfnFillRGBA(x, y, 1, h, 255, 0, 0, 255); gEngfuncs.pfnFillRGBA(x + w, y, 1, h, 255, 0, 0, 255); }
Не забываем добавить эту функцию в "public" "CHudStatusBar"а:
class CHudStatusBar : public CHudBase { public: ... void DrawBoundingBox(cl_entity_t* pEntity); ... }
Конечный результат можно украсить, нарисовав элементы рамки теми же спрайтами.
(!) Возможно при сборке компилятор поругается на отсутствие функций "min/max" в "DrawBoundingBox". Тогда в начале "cl_dll/statusbar.cpp" пишем:
#define min(a,b) ((a) < (b) ? (a) : (b)) #define max(a,b) ((a) > (b) ? (a) : (b))
Это решение есть в некоторых файлах Half-Life SDK.
(!) И возможно ещё потребно будет подключить "TriangleAPI":
#include "triangleapi.h"
На этом всё. Пишите, делитесь и т.д. и т.п. — будет интересно поглядеть.