1+ #include " CEntity.hh"
12#include " CItemInfo.hh"
3+ #include " CModelInfo.hh"
24#include " CStreaming.hh"
35#include " Utils.hh"
46#include " common/events.hh"
57#include " common/config.hh"
8+ #include " common/minhook.hh"
69#include " common/parser.hh"
710#include " common/logger.hh"
11+ #include " scrThread.hh"
812#include < cstdint>
13+ #include < ctime>
914#include < utility>
1015
16+ #include < CTheScripts.hh>
17+
1118#ifdef ENABLE_DEBUG_MENU
1219#include < debug/base.hh>
1320#endif
1421
22+ using namespace NativeLiterals ;
23+
1524// Ammo Info Randomizer
1625// *******************************************************
1726using CAmmoInfoRandomizer = ParserRandomHelper<
@@ -115,9 +124,68 @@ class WeaponStatsRandomizer
115124 HandleItemInfoRandomization (false );
116125 }
117126
127+ /* ******************************************************/
128+ template <auto &CProjectileEntity__PreSim>
129+ static int
130+ ProjectilePhysicsFix (CEntity *p1, float p2, bool p3, int p4)
131+ {
132+ /* Fixes a crash caused by delayed loading of the physics object for a
133+ * rocket projectile. This adds a null pointer check that R* forgot ..
134+ */
135+
136+ if (!p1->m_phInst )
137+ {
138+ Rainbomizer::Logger::LogMessage (
139+ " Projectile Physics Instance not loaded: %x" ,
140+ p1->m_pModelInfo ->m_nHash );
141+
142+ return 2 ;
143+ }
144+
145+ return CProjectileEntity__PreSim (p1, p2, p3, p4);
146+ }
147+
148+ /* ******************************************************/
149+ static void
150+ FixShootsAtCoord (scrThread::Info *ctx)
151+ {
152+ auto ped = ctx->GetArg (0 );
153+
154+ uint32_t weap = " GET_SELECTED_PED_WEAPON" _n (ped);
155+ auto *weapModel = CStreaming::GetModelByHash (weap);
156+
157+ static constexpr std::array BlackListedWeapons
158+ = {" prop_space_pistol" _joaat, " prop_space_rifle" _joaat,
159+ " ch_prop_collectibles_limb_01a" _joaat};
160+
161+ // We know this can happen with these weapons, so exit without logging
162+ if (DoesElementExist (BlackListedWeapons, weap))
163+ return ;
164+
165+ // In all other cases (e.g., it gets called for melee weapons randomized
166+ // into props), exit with a log message at a cooldown (so as to not spam
167+ // the log more than necessary).
168+ if (!weapModel
169+ || weapModel->GetType () != eModelInfoType::MODEL_INFO_WEAPON)
170+ {
171+ const int LOG_COOLDOWN = 5 ;
172+ static auto lastPrint = 0 ;
173+
174+ if (time (NULL ) - lastPrint > LOG_COOLDOWN)
175+ Rainbomizer::Logger::LogMessage (
176+ " SET_PED_SHOOTS_AT_COORD called for non-weapon model: "
177+ " %x %p" ,
178+ weap, weapModel);
179+ return ;
180+ }
181+
182+ NativeManager::InvokeNative (" SET_PED_SHOOTS_AT_COORD" _joaat, ctx);
183+ }
184+
118185 /* ******************************************************/
119186 WeaponStatsRandomizer ()
120187 {
188+
121189 if (!ConfigManager::ReadConfig (" WeaponStatsRandomizer" ))
122190 return ;
123191
@@ -128,6 +196,10 @@ class WeaponStatsRandomizer
128196 DebugInterfaceManager::AddAction (" Randomize Weapon Stats" ,
129197 RandomizeWeaponStats);
130198#endif
199+ REGISTER_MH_HOOK (
200+ " ? 0f 28 e1 ? 8b 41 30 b9 ff ff 00 00 66 39 48 18 75 ?" , -0x56 ,
201+ ProjectilePhysicsFix, int , CEntity *, float , bool , int );
202+
131203 InitialiseAllComponents ();
132204 }
133205} _stats;
0 commit comments