Skip to content

Commit b89fc7e

Browse files
committed
Add crosshair component
1 parent b28f513 commit b89fc7e

3 files changed

Lines changed: 244 additions & 37 deletions

File tree

Client/game_sa/CHudSA.cpp

Lines changed: 191 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ std::unordered_map<eHudComponent, SHudComponentData> defaultComponentProperties
5858
{HUD_WEAPON, {RwColor{255, 255, 255, 255}, RwColor{255, 255, 255, 255}}},
5959
{HUD_WANTED,
6060
{CHudSA::GetHUDColour(eHudColour::GOLD), RwColor{0, 0, 0, 170}, false, false, COLOR_BLACK, eFontAlignment::ALIGN_RIGHT, eFontStyle::FONT_GOTHIC, 1, 0,
61-
true}}};
61+
true}},
62+
{HUD_CROSSHAIR, RwColor{255, 255, 255, 255}}};
6263

6364
CHudSA::CHudSA()
6465
{
@@ -94,6 +95,7 @@ CHudSA::CHudSA()
9495
componentProperties.radioName = MapGet(defaultComponentProperties, HUD_RADIO);
9596
componentProperties.weaponIcon = MapGet(defaultComponentProperties, HUD_WEAPON);
9697
componentProperties.wanted = MapGet(defaultComponentProperties, HUD_WANTED);
98+
componentProperties.crosshair = MapGet(defaultComponentProperties, HUD_CROSSHAIR);
9799
}
98100

99101
void CHudSA::Disable(bool bDisabled)
@@ -266,6 +268,11 @@ void CHudSA::UpdateStreetchCalculations()
266268
wantedPlacement.height = calcStreetchY * 1.21f;
267269
wantedPlacement.width = calcStreetchX * 0.6f;
268270
wantedPlacement.setDefaultXY = false;
271+
272+
SComponentPlacement& crosshairPlacement = componentProperties.crosshair.placement;
273+
crosshairPlacement.height = 0;
274+
crosshairPlacement.width = 0;
275+
crosshairPlacement.setDefaultXY = false;
269276
}
270277

271278
//
@@ -351,9 +358,7 @@ bool CHudSA::IsCrosshairVisible()
351358
}
352359
}
353360

354-
// Check CTheScripts::bDrawCrossHair
355-
std::uint8_t crossHairType = *reinterpret_cast<std::uint8_t*>(VAR_CTheScripts_bDrawCrossHair);
356-
return specialAiming || simpleAiming || crossHairType > 0;
361+
return specialAiming || simpleAiming;
357362
}
358363

359364
bool CHudSA::IsComponentBar(const eHudComponent& component) const noexcept
@@ -531,6 +536,8 @@ SHudComponentData& CHudSA::GetHudComponentRef(const eHudComponent& component) co
531536
return componentProperties.weaponIcon;
532537
case HUD_WANTED:
533538
return componentProperties.wanted;
539+
case HUD_CROSSHAIR:
540+
return componentProperties.crosshair;
534541
default:
535542
break;
536543
}
@@ -594,6 +601,12 @@ CVector2D CHudSA::GetComponentPosition(const eHudComponent& component) const noe
594601
float x = ref.placement.useCustomPosition ? ref.placement.customX : ref.placement.x;
595602
float y = ref.placement.useCustomPosition ? ref.placement.customY : ref.placement.y;
596603

604+
if (component == eHudComponent::HUD_CROSSHAIR && ref.placement.useCustomPosition)
605+
{
606+
x -= ref.placement.width;
607+
y -= ref.placement.height;
608+
}
609+
597610
return CVector2D(x, y);
598611
}
599612

@@ -604,6 +617,18 @@ CVector2D CHudSA::GetComponentSize(const eHudComponent& component) const noexcep
604617
float w = ref.placement.useCustomSize ? ref.placement.customWidth : ref.placement.width;
605618
float h = ref.placement.useCustomSize ? ref.placement.customHeight : ref.placement.height;
606619

620+
if (component == eHudComponent::HUD_CROSSHAIR)
621+
{
622+
float offsetW = ref.placement.width - w;
623+
float offsetH = ref.placement.height - h;
624+
625+
w += offsetW;
626+
h += offsetH;
627+
628+
w *= 2.0f;
629+
h *= 2.0f;
630+
}
631+
607632
return CVector2D(w, h);
608633
}
609634

@@ -655,14 +680,14 @@ void CHudSA::RenderHealthBar(int x, int y)
655680
float barWidth = useCustomSize ? componentProperties.hpBar.placement.customWidth : componentProperties.hpBar.placement.width;
656681

657682
// Calc bar width depending on MAX_HEALTH stat
658-
const double statModifier = ((double(__cdecl*)(int))FUNC_CStats_GetFatAndMuscleModifier)(10);
683+
double statModifier = ((double(__cdecl*)(int))FUNC_CStats_GetFatAndMuscleModifier)(10);
659684
if (statModifier <= 0.0)
660685
return;
661686

662-
const float totalWidth = static_cast<float>((static_cast<double>(barWidth) * maxHealth) / statModifier);
687+
float totalWidth = static_cast<float>((static_cast<double>(barWidth) * maxHealth) / statModifier);
663688

664-
const float posX = useCustomPosition ? componentProperties.hpBar.placement.customX : (barWidth - totalWidth + static_cast<float>(x));
665-
const float posY = useCustomPosition ? componentProperties.hpBar.placement.customY : static_cast<float>(y);
689+
float posX = useCustomPosition ? componentProperties.hpBar.placement.customX : (barWidth - totalWidth + static_cast<float>(x));
690+
float posY = useCustomPosition ? componentProperties.hpBar.placement.customY : static_cast<float>(y);
666691
std::uint32_t barHeight =
667692
static_cast<std::uint32_t>(useCustomSize ? componentProperties.hpBar.placement.customHeight : componentProperties.hpBar.placement.height);
668693

@@ -698,15 +723,15 @@ void CHudSA::RenderBreathBar(int x, int y)
698723
bool useCustomPosition = componentProperties.breathBar.placement.useCustomPosition;
699724
bool useCustomSize = componentProperties.breathBar.placement.useCustomSize;
700725

701-
const float posX = useCustomPosition ? componentProperties.breathBar.placement.customX : static_cast<float>(x);
702-
const float posY = useCustomPosition ? componentProperties.breathBar.placement.customY : static_cast<float>(y);
726+
float posX = useCustomPosition ? componentProperties.breathBar.placement.customX : static_cast<float>(x);
727+
float posY = useCustomPosition ? componentProperties.breathBar.placement.customY : static_cast<float>(y);
703728
std::uint16_t barWidth =
704729
static_cast<std::uint16_t>(useCustomSize ? componentProperties.breathBar.placement.customWidth : componentProperties.breathBar.placement.width);
705730
std::uint32_t barHeight =
706731
static_cast<std::uint32_t>(useCustomSize ? componentProperties.breathBar.placement.customHeight : componentProperties.breathBar.placement.height);
707732

708733
// call CSprite2d::DrawBarChart
709-
const float breathPercent = static_cast<float>((static_cast<double>(playerPed->GetOxygenLevel()) / statModifier) * 100.0);
734+
float breathPercent = static_cast<float>((static_cast<double>(playerPed->GetOxygenLevel()) / statModifier) * 100.0);
710735
DrawBarChart(posX, posY, barWidth, barHeight, breathPercent, false, componentProperties.breathBar.drawPercentage,
711736
componentProperties.breathBar.drawBlackBorder, componentProperties.breathBar.fillColor, COLOR_BLACK);
712737
}
@@ -733,8 +758,8 @@ void CHudSA::RenderArmorBar(int x, int y)
733758
bool useCustomPosition = componentProperties.hpBar.placement.useCustomPosition;
734759
bool useCustomSize = componentProperties.hpBar.placement.useCustomSize;
735760

736-
const float posX = useCustomPosition ? componentProperties.armorBar.placement.customX : static_cast<float>(x);
737-
const float posY = useCustomPosition ? componentProperties.armorBar.placement.customY : static_cast<float>(y);
761+
float posX = useCustomPosition ? componentProperties.armorBar.placement.customX : static_cast<float>(x);
762+
float posY = useCustomPosition ? componentProperties.armorBar.placement.customY : static_cast<float>(y);
738763
std::uint16_t barWidth =
739764
static_cast<std::uint16_t>(useCustomSize ? componentProperties.armorBar.placement.customWidth : componentProperties.armorBar.placement.width);
740765
std::uint32_t barHeight =
@@ -928,6 +953,137 @@ void CHudSA::RenderWanted(bool empty, float x, float y, const char* strLevel)
928953
RenderText(x, y, strLevel, componentProperties.wanted, empty);
929954
}
930955

956+
void __fastcall CHudSA::RenderCircleCrosshair(void* sprite, void*, CRect* rect, RwColor* color)
957+
{
958+
float screenChairX = rsGlobal->maximumWidth * *reinterpret_cast<float*>(VAR_ChairMultX);
959+
float screenChairY = rsGlobal->maximumHeight * *reinterpret_cast<float*>(VAR_ChairMultY);
960+
961+
CPed* playerPed = pGame->GetPedContext();
962+
if (!playerPed)
963+
return;
964+
965+
// Call CPlayerPed::GetWeaponRadiusOnScreen
966+
double gunRadius = ((double(__thiscall*)(CPedSAInterface*))0x609CD0)(playerPed->GetPedInterface());
967+
968+
SHudComponentData& properties = componentProperties.crosshair;
969+
970+
auto DrawSprite = [&properties](void* sprite, CRect* rect) -> void
971+
{
972+
// Call CSprite2d::Draw
973+
((void(__thiscall*)(void*, CRect*, RwColor*))FUNC_CSprite2d_Draw)(sprite, rect, &properties.fillColor);
974+
};
975+
976+
bool useCustomPosition = properties.placement.useCustomPosition;
977+
bool useCustomSize = properties.placement.useCustomSize;
978+
979+
float width = calcStreetchX * VAR_CrosshairTexSize * gunRadius * 0.5f; // width of one sprite
980+
float height = calcStreetchY * VAR_CrosshairTexSize * gunRadius * 0.5f; // height of one sprite
981+
982+
float x = useCustomPosition ? properties.placement.customX : screenChairX;
983+
float y = useCustomPosition ? properties.placement.customY : screenChairY;
984+
float w = useCustomSize ? properties.placement.customWidth : width;
985+
float h = useCustomSize ? properties.placement.customHeight : height;
986+
properties.placement.width = w;
987+
properties.placement.height = h;
988+
989+
rect->left = x - w;
990+
properties.placement.x = rect->left;
991+
992+
rect->right = x;
993+
rect->bottom = y;
994+
rect->top = y - h;
995+
properties.placement.y = rect->top;
996+
997+
// left top
998+
DrawSprite(sprite, rect);
999+
1000+
// right top
1001+
rect->left = x + w;
1002+
DrawSprite(sprite, rect);
1003+
1004+
// left bottom
1005+
rect->left = x - w;
1006+
rect->bottom = y;
1007+
rect->top = y + h;
1008+
DrawSprite(sprite, rect);
1009+
1010+
// right bottom
1011+
rect->left = x + w;
1012+
DrawSprite(sprite, rect);
1013+
}
1014+
1015+
void CHudSA::RenderRocketCrosshair(CVector _pos, CVector2D _halfSize, std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint16_t intensity, float rhw,
1016+
std::uint8_t a, std::uint8_t uDir, std::uint8_t vDir)
1017+
{
1018+
CPed* playerPed = pGame->GetPedContext();
1019+
if (!playerPed)
1020+
return;
1021+
1022+
SHudComponentData& properties = componentProperties.crosshair;
1023+
1024+
auto DrawXLUSprite = [&properties](CVector& pos, CVector2D& halfSize, std::uint8_t uDir, std::uint8_t vDir) -> void
1025+
{
1026+
// Call CSprite::RenderOneXLUSprite
1027+
((void(__cdecl*)(CVector, CVector2D, std::uint8_t, std::uint8_t, std::uint8_t, std::uint16_t, float, std::uint8_t, std::uint8_t,
1028+
std::uint8_t))FUNC_CSprite_RenderOneXLUSprite)(pos, halfSize, properties.fillColor.r, properties.fillColor.g, properties.fillColor.b,
1029+
static_cast<std::uint16_t>(properties.fillColor.a), 0.01f, properties.fillColor.a, uDir,
1030+
vDir);
1031+
};
1032+
1033+
CVector2D screenCrossHair{};
1034+
CVector2D screenOffsetCenter{0.0f, 0.0f};
1035+
1036+
bool useCustomPosition = properties.placement.useCustomPosition;
1037+
bool useCustomSize = properties.placement.useCustomSize;
1038+
1039+
eWeaponType weaponType = playerPed->GetWeapon(playerPed->GetCurrentWeaponSlot())->GetType();
1040+
if (weaponType == eWeaponType::WEAPONTYPE_CAMERA || weaponType == eWeaponType::WEAPONTYPE_SNIPERRIFLE)
1041+
{
1042+
if (weaponType == eWeaponType::WEAPONTYPE_CAMERA)
1043+
{
1044+
screenCrossHair.fX = useCustomSize ? properties.placement.customWidth : calcStreetchX * 256.0f;
1045+
screenCrossHair.fY = useCustomSize ? properties.placement.customHeight
1046+
: calcStreetchY * *reinterpret_cast<float*>(VAR_CameraCrosshairScale); // m_pfCameraCrosshairScale
1047+
}
1048+
else
1049+
{
1050+
float sniperCrosshairScale = **reinterpret_cast<float**>(0x58E7D6); // m_fSniperCrosshairScale
1051+
screenCrossHair.fX = useCustomSize ? properties.placement.customWidth : calcStreetchX * sniperCrosshairScale;
1052+
screenCrossHair.fY = useCustomSize ? properties.placement.customHeight : calcStreetchY * sniperCrosshairScale;
1053+
}
1054+
}
1055+
else
1056+
{
1057+
screenCrossHair.fX = useCustomSize ? properties.placement.customWidth : calcStreetchX * 24.0f;
1058+
screenCrossHair.fY = useCustomSize ? properties.placement.customHeight : calcStreetchY * 24.0f;
1059+
screenOffsetCenter.fX = calcStreetchX * 20.0f;
1060+
screenOffsetCenter.fY = calcStreetchY * 20.0f;
1061+
}
1062+
1063+
CVector2D screenCenter = {rsGlobal->maximumWidth * 0.5f, rsGlobal->maximumHeight * 0.5f};
1064+
CVector2D halfSize = {screenCrossHair.fX * 0.5f, screenCrossHair.fY * 0.5f};
1065+
properties.placement.width = screenCrossHair.fX + screenOffsetCenter.fX;
1066+
properties.placement.height = screenCrossHair.fY + screenOffsetCenter.fY;
1067+
1068+
float x = useCustomPosition ? properties.placement.customX : screenCenter.fX;
1069+
float y = useCustomPosition ? properties.placement.customY : screenCenter.fY;
1070+
1071+
CVector leftTop = CVector(x - halfSize.fX - screenOffsetCenter.fX, y - halfSize.fY - screenOffsetCenter.fY, 1.0f);
1072+
properties.placement.x = leftTop.fX - halfSize.fX;
1073+
properties.placement.y = leftTop.fY - halfSize.fY;
1074+
1075+
DrawXLUSprite(leftTop, halfSize, 0, 0);
1076+
1077+
CVector rightTop = CVector(x + halfSize.fX + screenOffsetCenter.fX, y - halfSize.fY - screenOffsetCenter.fY, 1.0f);
1078+
DrawXLUSprite(rightTop, halfSize, 1, 0);
1079+
1080+
CVector leftBottom = CVector(x - halfSize.fX - screenOffsetCenter.fX, y + halfSize.fY + screenOffsetCenter.fY, 1.0f);
1081+
DrawXLUSprite(leftBottom, halfSize, 0, 1);
1082+
1083+
CVector rightBottom = CVector(x + halfSize.fX + screenOffsetCenter.fX, y + halfSize.fY + screenOffsetCenter.fY, 1.0f);
1084+
DrawXLUSprite(rightBottom, halfSize, 1, 1);
1085+
}
1086+
9311087
static constexpr std::uintptr_t CONTINUE_RenderWanted = 0x58DFD8;
9321088
static void __declspec(naked) HOOK_RenderWanted()
9331089
{
@@ -971,16 +1127,29 @@ void CHudSA::StaticSetHooks()
9711127
HookInstall(FUNC_RenderBreathBar, &HOOK_RenderHudBar, 11);
9721128
HookInstall(FUNC_RenderArmorBar, &HOOK_RenderHudBar, 11);
9731129

974-
HookInstallCall(0x58EC21, (DWORD)&RenderClock);
975-
HookInstallCall(0x58F607, (DWORD)&RenderMoney);
976-
HookInstallCall(0x58962A, (DWORD)&RenderAmmo);
1130+
HookInstallCall(0x58EC21, (DWORD)RenderClock);
1131+
HookInstallCall(0x58F607, (DWORD)RenderMoney);
1132+
HookInstallCall(0x58962A, (DWORD)RenderAmmo);
1133+
1134+
HookInstallCall(0x58B156, (DWORD)RenderVehicleName);
1135+
HookInstallCall(0x58AE5D, (DWORD)RenderZoneName);
1136+
HookInstallCall(0x4E9FF1, (DWORD)RenderRadioName);
1137+
1138+
HookInstallCall(0x58D988, (DWORD)RenderWeaponIcon_Sprite);
1139+
HookInstallCall(0x58D8FD, (DWORD)RenderWeaponIcon_XLU);
1140+
1141+
HookInstall(0x58DFD3, HOOK_RenderWanted);
9771142

978-
HookInstallCall(0x58B156, (DWORD)&RenderVehicleName);
979-
HookInstallCall(0x58AE5D, (DWORD)&RenderZoneName);
980-
HookInstallCall(0x4E9FF1, (DWORD)&RenderRadioName);
1143+
HookInstallCall(0x58E3A5, (DWORD)RenderCircleCrosshair);
1144+
HookInstallCall(0x58E95B, (DWORD)RenderRocketCrosshair);
9811145

982-
HookInstallCall(0x58D988, (DWORD)&RenderWeaponIcon_Sprite);
983-
HookInstallCall(0x58D8FD, (DWORD)&RenderWeaponIcon_XLU);
1146+
// Disable circle crosshair rendering by GTA
1147+
MemCpy((void*)0x58E3FA, "\x83\xC4\x08\x90\x90", 5);
1148+
MemCpy((void*)0x58E44F, "\x83\xC4\x08\x90\x90", 5);
1149+
MemCpy((void*)0x58E49C, "\x83\xC4\x08\x90\x90", 5);
9841150

985-
HookInstall(0x58DFD3, &HOOK_RenderWanted);
1151+
// Disable rocket/sniper/camera crosshair rendering by GTA
1152+
MemSet((void*)0x58E9CA, 0x90, 5);
1153+
MemSet((void*)0x58EA39, 0x90, 5);
1154+
MemSet((void*)0x58EAA8, 0x90, 5);
9861155
}

Client/game_sa/CHudSA.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@
5555
#define CODE_ShowMoney 0x58F47D
5656
#define CODE_ShowRadarAltimeter 0x58A5A6
5757

58-
#define VAR_CTheScripts_bDrawCrossHair 0xA44490
59-
#define VAR_RSGlobal 0xC17040
60-
#define VAR_ItemToFlash 0xBAB1DC
58+
#define VAR_RSGlobal 0xC17040
59+
#define VAR_ItemToFlash 0xBAB1DC
60+
61+
#define VAR_ChairMultX 0xB6EC14
62+
#define VAR_ChairMultY 0xB6EC10
63+
64+
#define VAR_CrosshairTexSize 64.0f // 64x64
6165

6266
struct SHudComponent
6367
{
@@ -170,6 +174,8 @@ struct ComponentProperties
170174

171175
SHudComponentData weaponIcon;
172176
SHudComponentData wanted;
177+
178+
SHudComponentData crosshair;
173179
};
174180

175181
class CHudSA : public CHud
@@ -297,6 +303,10 @@ class CHudSA : public CHud
297303

298304
static void RenderWanted(bool empty, float x, float y, const char* strLevel);
299305

306+
static void __fastcall RenderCircleCrosshair(void* sprite, void*, CRect* rect, RwColor* color);
307+
static void RenderRocketCrosshair(CVector pos, CVector2D halfSize, std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint16_t intensity, float rhw,
308+
std::uint8_t a, std::uint8_t uDir, std::uint8_t vDir);
309+
300310
private:
301311
std::map<eHudComponent, SHudComponent> m_HudComponentMap;
302312

0 commit comments

Comments
 (0)