@@ -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
6364CHudSA::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
99101void 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
359364bool 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+
9311087static constexpr std::uintptr_t CONTINUE_RenderWanted = 0x58DFD8 ;
9321088static 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}
0 commit comments