44#include " LuaInterface.h"
55#include " lua.h"
66#include " player.h"
7+ #define private public
8+ #include " soundscape_system.h"
9+ #include " soundscape.h"
10+ #undef private
711
812// memdbgon must be the last include file in a .cpp file!!!
913#include " tier0/memdbgon.h"
@@ -24,6 +28,8 @@ class CSoundscapeModule : public IModule
2428static CSoundscapeModule g_pSoundscapeModule;
2529IModule* pSoundscapeModule = &g_pSoundscapeModule;
2630
31+ static ConVar soundscape_updateplayerhook (" holylib_soundscape_updateplayerhook" , " 0" , FCVAR_ARCHIVE, " If enabled, the soundscape hooks are called." );
32+
2733static DTVarByOffset m_Local_Offset (" DT_LocalPlayerExclusive" , " m_Local" );
2834static DTVarByOffset m_Audio_Offset (" DT_Local" , " m_audio.localSound[0]" );
2935static inline audioparams_t * GetAudioParams (void * pPlayer)
@@ -85,6 +91,163 @@ LUA_FUNCTION_STATIC(soundscape_GetActivePositions)
8591 return 1 ;
8692}
8793
94+ static CSoundscapeSystem* g_pSoundscapeSystem = nullptr ;
95+ LUA_FUNCTION_STATIC (soundscape_GetAll)
96+ {
97+ if (!g_pSoundscapeSystem)
98+ LUA->ThrowError (" Failed to load g_pSoundscapeSystem!" );
99+
100+ LUA->CreateTable ();
101+
102+ for (auto nIndex=g_pSoundscapeSystem->m_soundscapes .First (); nIndex != g_pSoundscapeSystem->m_soundscapes .InvalidIndex (); nIndex = g_pSoundscapeSystem->m_soundscapes .Next (nIndex))
103+ {
104+ LUA->PushString (g_pSoundscapeSystem->m_soundscapes .GetStringForKey (nIndex));
105+ LUA->PushNumber (g_pSoundscapeSystem->m_soundscapes .GetIDForKey (nIndex));
106+
107+ LUA->RawSet (-3 );
108+ }
109+
110+ return 1 ;
111+ }
112+
113+ static bool IsValidStringIndex (int nIndex)
114+ {
115+ if (!g_pSoundscapeSystem)
116+ return false ;
117+
118+ return nIndex >= 0 && nIndex < g_pSoundscapeSystem->m_soundscapeCount ;
119+ }
120+
121+ LUA_FUNCTION_STATIC (soundscape_GetNameByIndex)
122+ {
123+ if (!g_pSoundscapeSystem)
124+ LUA->ThrowError (" Failed to load g_pSoundscapeSystem!" );
125+
126+ int nIndex = LUA->CheckNumber (1 );
127+ if (!IsValidStringIndex (nIndex))
128+ {
129+ LUA->PushNil ();
130+ return 1 ;
131+ }
132+
133+ const char * pName = g_pSoundscapeSystem->m_soundscapes .GetStringText (nIndex);
134+ if (!pName)
135+ {
136+ LUA->PushNil ();
137+ return 1 ;
138+ }
139+
140+ LUA->PushString (pName);
141+ return 1 ;
142+ }
143+
144+ LUA_FUNCTION_STATIC (soundscape_GetIndexByName)
145+ {
146+ if (!g_pSoundscapeSystem)
147+ LUA->ThrowError (" Failed to load g_pSoundscapeSystem!" );
148+
149+ const char * pName = LUA->CheckString (1 );
150+
151+ int nIndex = g_pSoundscapeSystem->m_soundscapes .GetStringID (pName);
152+ if (IsValidStringIndex (nIndex))
153+ LUA->PushNumber (nIndex);
154+ else
155+ LUA->PushNil ();
156+
157+ return 1 ;
158+ }
159+
160+ LUA_FUNCTION_STATIC (soundscape_GetAllEntities)
161+ {
162+ if (!g_pSoundscapeSystem)
163+ LUA->ThrowError (" Failed to load g_pSoundscapeSystem!" );
164+
165+ int nCount = g_pSoundscapeSystem->m_soundscapeEntities .Count ();
166+ LUA->PreCreateTable (nCount, 0 );
167+
168+ int nLuaIndex = 0 ;
169+ for (int entityIndex = 0 ; entityIndex < nCount; ++entityIndex)
170+ {
171+ Util::Push_Entity (LUA, g_pSoundscapeSystem->m_soundscapeEntities [entityIndex]);
172+ Util::RawSetI (LUA, -2 , ++nLuaIndex);
173+ }
174+
175+ return 1 ;
176+ }
177+
178+ static int nLastUpdateTick = -1 ;
179+ static std::unordered_set<CBasePlayer*> pHandledPlayers;
180+ static ss_update_t * pCurrentUpdate = nullptr ;
181+ static Detouring::Hook detour_CEnvSoundscape_UpdateForPlayer;
182+ static Symbols::CEnvSoundscape_WriteAudioParamsTo func_CEnvSoundscape_WriteAudioParamsTo = nullptr ;
183+ static void hook_CEnvSoundscape_UpdateForPlayer (CBaseEntity* pSoundScape, ss_update_t & update)
184+ {
185+ int nTick = gpGlobals->tickcount ;
186+ if (nTick != nLastUpdateTick)
187+ pHandledPlayers.clear ();
188+
189+ // Player got handled by Lua already, so we can skip
190+ if (update.pPlayer && pHandledPlayers.find (update.pPlayer ) != pHandledPlayers.end ())
191+ return ;
192+
193+ // Can update.pPlayer even be NULL? Probably no
194+ if (soundscape_updateplayerhook.GetBool () && update.pPlayer && Lua::PushHook (" HolyLib:OnSoundScapeUpdateForPlayer" ))
195+ {
196+ pCurrentUpdate = &update;
197+ if (g_Lua->CallFunctionProtected (1 , 1 , true ))
198+ {
199+ bool bCancel = g_Lua->GetBool (-1 );
200+ g_Lua->Pop (1 );
201+
202+ if (bCancel)
203+ {
204+ audioparams_t * pParams = GetAudioParams (update.pPlayer );
205+ if (pParams && func_CEnvSoundscape_WriteAudioParamsTo)
206+ func_CEnvSoundscape_WriteAudioParamsTo (update.pCurrentSoundscape , *pParams);
207+
208+ // We canceled, so we now, we don't want anything to mess with the player further, so we'll block further actions for this tick.
209+ pHandledPlayers.insert (update.pPlayer );
210+ pCurrentUpdate = nullptr ;
211+ return ;
212+ }
213+ }
214+ pCurrentUpdate = nullptr ;
215+ }
216+
217+ detour_CEnvSoundscape_UpdateForPlayer.GetTrampoline <Symbols::CEnvSoundscape_UpdateForPlayer>()(pSoundScape, update);
218+ }
219+
220+ LUA_FUNCTION_STATIC (soundscape_SetCurrentDistance)
221+ {
222+ if (!pCurrentUpdate)
223+ LUA->ThrowError (" Tried to use this function outside of a Soundscape hook call!" );
224+
225+ // If one lets the Hook continue/doesn't cancel from Lua, then this can influence which other soundscape might get picked
226+ pCurrentUpdate->currentDistance = LUA->CheckNumber (1 );
227+ pCurrentUpdate->bInRange = LUA->GetBool (2 );
228+ return 0 ;
229+ }
230+
231+ // This basically is the only one realisticly useful
232+ // SetCurrentDistance & SetCurrentPlayerPosition are just to influence the soundscape selection
233+ LUA_FUNCTION_STATIC (soundscape_SetCurrentSoundscape)
234+ {
235+ if (!pCurrentUpdate)
236+ LUA->ThrowError (" Tried to use this function outside of a Soundscape hook call!" );
237+
238+ pCurrentUpdate->pCurrentSoundscape = (CEnvSoundscape*)Util::Get_Entity (LUA, 1 , true );
239+ return 0 ;
240+ }
241+
242+ LUA_FUNCTION_STATIC (soundscape_SetCurrentPlayerPosition)
243+ {
244+ if (!pCurrentUpdate)
245+ LUA->ThrowError (" Tried to use this function outside of a Soundscape hook call!" );
246+
247+ pCurrentUpdate->playerPosition = *Get_Vector (LUA, 1 , true );
248+ return 0 ;
249+ }
250+
88251void CSoundscapeModule::Init (CreateInterfaceFn* appfn, CreateInterfaceFn* gamefn)
89252{
90253}
@@ -94,6 +257,18 @@ void CSoundscapeModule::InitDetour(bool bPreServer)
94257 if (bPreServer)
95258 return ;
96259
260+ SourceSDK::FactoryLoader server_loader (" server" );
261+ Detour::Create (
262+ &detour_CEnvSoundscape_UpdateForPlayer, " CEnvSoundscape::UpdateForPlayer" ,
263+ server_loader.GetModule (), Symbols::CEnvSoundscape_UpdateForPlayerSym,
264+ (void *)hook_CEnvSoundscape_UpdateForPlayer, m_pID
265+ );
266+
267+ g_pSoundscapeSystem = Detour::ResolveSymbol<CSoundscapeSystem>(server_loader, Symbols::g_SoundscapeSystemSym);
268+ Detour::CheckValue (" get class" , " g_SoundscapeSystem" , g_pSoundscapeSystem != nullptr );
269+
270+ func_CEnvSoundscape_WriteAudioParamsTo = (Symbols::CEnvSoundscape_WriteAudioParamsTo)Detour::GetFunction (server_loader.GetModule (), Symbols::CEnvSoundscape_WriteAudioParamsToSym);
271+ Detour::CheckFunction ((void *)func_CEnvSoundscape_WriteAudioParamsTo, " CEnvSoundscape::WriteAudioParamsTo" );
97272}
98273
99274void CSoundscapeModule::LuaInit (GarrysMod::Lua::ILuaInterface* pLua, bool bServerInit)
@@ -102,6 +277,15 @@ void CSoundscapeModule::LuaInit(GarrysMod::Lua::ILuaInterface* pLua, bool bServe
102277 Util::AddFunc (pLua, soundscape_GetActiveSoundScape, " GetActiveSoundScape" );
103278 Util::AddFunc (pLua, soundscape_GetActiveSoundScapeIndex, " GetActiveSoundScapeIndex" );
104279 Util::AddFunc (pLua, soundscape_GetActivePositions, " GetActivePositions" );
280+
281+ Util::AddFunc (pLua, soundscape_GetAll, " GetAll" );
282+ Util::AddFunc (pLua, soundscape_GetNameByIndex, " GetNameByIndex" );
283+ Util::AddFunc (pLua, soundscape_GetIndexByName, " GetIndexByName" );
284+ Util::AddFunc (pLua, soundscape_GetAllEntities, " GetAllEntities" );
285+
286+ Util::AddFunc (pLua, soundscape_SetCurrentDistance, " SetCurrentDistance" );
287+ Util::AddFunc (pLua, soundscape_SetCurrentSoundscape, " SetCurrentSoundscape" );
288+ Util::AddFunc (pLua, soundscape_SetCurrentPlayerPosition, " SetCurrentPlayerPosition" );
105289 Util::FinishTable (pLua, " soundscape" );
106290}
107291
0 commit comments