Автор: arbtttrn6
Суть: Рисование рамки вокруг сущности (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(pEntity->pev->health)); // здоровье сущности
WRITE_SHORT(static_cast(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(minX);
int y = static_cast(minY);
int w = static_cast(maxX - minX);
int h = static_cast(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"
На этом всё. Пишите, делитесь и т.д. и т.п. — будет интересно поглядеть.