22#pragma newdecls required
33
44#define DEBUG 1
5- #define PLUGIN_VERSION " 2.0 "
5+ #define PLUGIN_VERSION " 2.1 "
66#define PLUGIN_TAG " l4d_consistent_escaperoute"
77
88#include <sourcemod>
99#include <dhooks>
1010#include <sourcescramble>
11+ #include <sdktools>
12+ #include <left4dhooks>
1113
1214#if DEBUG
1315#include <sdktools_functions>
@@ -22,6 +24,13 @@ public Plugin myinfo =
2224 url = " https://github.com/Target5150/MoYu_Server_Stupid_Plugins"
2325};
2426
27+ enum struct SDKCallParamsWrapper {
28+ SDKType type ;
29+ SDKPassMethod pass ;
30+ int decflags ;
31+ int encflags ;
32+ }
33+
2534methodmap GameDataWrapper < GameData {
2635 public GameDataWrapper (const char [] file ) {
2736 GameData gd = new GameData (file );
@@ -60,6 +69,27 @@ methodmap GameDataWrapper < GameData {
6069 SetFailState (" Failed to post-detour \" %s \" " , name );
6170 return hSetup ;
6271 }
72+ public Handle CreateSDKCallOrFail (
73+ SDKCallType type ,
74+ SDKFuncConfSource src ,
75+ const char [] name ,
76+ const SDKCallParamsWrapper [] params = {},
77+ int numParams = 0 ,
78+ bool hasReturnValue = false ,
79+ const SDKCallParamsWrapper ret = {}) {
80+ static const char k_sSDKFuncConfSource [SDKFuncConfSource ][] = { " offset" , " signature" , " address" };
81+ Handle result ;
82+ StartPrepSDKCall (type );
83+ if (! PrepSDKCall_SetFromConf (this , src , name ))
84+ SetFailState (" Missing %s \" %s \" " , k_sSDKFuncConfSource [src ], name );
85+ for (int i = 0 ; i < numParams ; ++ i )
86+ PrepSDKCall_AddParameter (params [i ].type , params [i ].pass , params [i ].decflags , params [i ].encflags );
87+ if (hasReturnValue )
88+ PrepSDKCall_SetReturnInfo (ret .type , ret .pass , ret .decflags , ret .encflags );
89+ if (! (result = EndPrepSDKCall ()))
90+ SetFailState (" Failed to prep sdkcall \" %s \" " , name );
91+ return result ;
92+ }
6393}
6494
6595methodmap Address {}
@@ -98,6 +128,7 @@ methodmap TerrorNavArea < Address
98128#endif
99129
100130MemoryPatch g_patch_SkipSpawnPosIdx ;
131+ Handle g_call_Checkpoint_GetLargestArea ;
101132
102133public void OnPluginStart ()
103134{
@@ -110,6 +141,12 @@ public void OnPluginStart()
110141
111142 g_patch_SkipSpawnPosIdx = gd .CreatePatchOrFail (" skip_spawn_position_idx" , false );
112143
144+ SDKCallParamsWrapper params [] = {
145+ { SDKType_PlainOldData , SDKPass_Plain },
146+ };
147+ g_call_Checkpoint_GetLargestArea = gd .CreateSDKCallOrFail (SDKCall_Raw , SDKConf_Signature , " Checkpoint::GetLargestArea" , _ , 0 , true , params [0 ]);
148+
149+ delete gd .CreateDetourOrFail (" l4d_consistent_escaperoute::Checkpoint::GetSpawnPosition" , _ , DTR_Checkpoint_GetSpawnPosition_Post );
113150 delete gd .CreateDetourOrFail (" l4d_consistent_escaperoute::GetPlayerSpawnPosition" , DTR_GetPlayerSpawnPosition , DTR_GetPlayerSpawnPosition_Post );
114151 delete gd .CreateDetourOrFail (" l4d_consistent_escaperoute::TerrorNavMesh::ComputeFlowDistances" , DTR_ComputeFlowDistances , DTR_ComputeFlowDistances_Post );
115152 delete gd ;
@@ -136,6 +173,7 @@ MRESReturn DTR_ComputeFlowDistances(DHookReturn hReturn)
136173 return MRES_Ignored ;
137174}
138175
176+ // GetPlayerSpawnPosition(SurvivorCharacterType,Vector *,QAngle *,TerrorNavArea **)
139177MRESReturn DTR_GetPlayerSpawnPosition (DHookReturn hReturn , DHookParam hParams )
140178{
141179 if (! g_bInComputeFlowDistances )
@@ -159,6 +197,49 @@ MRESReturn DTR_GetPlayerSpawnPosition(DHookReturn hReturn, DHookParam hParams)
159197 return MRES_Ignored ;
160198}
161199
200+ // Checkpoint::GetSpawnPosition(Vector *, QAngle *, TerrorNavArea **)const
201+ MRESReturn DTR_Checkpoint_GetSpawnPosition_Post (Address pThis , DHookReturn hReturn , DHookParam hParams )
202+ {
203+ if (! g_bInComputeFlowDistances )
204+ return MRES_Ignored ;
205+
206+ #if DEBUG
207+ PrintToServer (" [%s ] Overriding Checkpoint::GetSpawnPosition" , PLUGIN_TAG );
208+ #endif
209+
210+ // NOTE:
211+ // GetSpawnPosition always computes a random position within start safe area.
212+ // The following makes it always return the largest area instead.
213+ TerrorNavArea area = SDKCall (g_call_Checkpoint_GetLargestArea , pThis );
214+ if (area == Address_Null )
215+ return MRES_Ignored ;
216+
217+ #if DEBUG
218+ PrintToServer (" [%s ] Fixed player start area = %d " , PLUGIN_TAG , area .GetID ());
219+ #endif
220+
221+ if (! hParams .IsNull (1 ))
222+ {
223+ float pos [3 ];
224+ L4D_GetNavAreaCenter (area , pos );
225+ hParams .SetVector (1 , pos );
226+ }
227+
228+ if (! hParams .IsNull (2 ))
229+ {
230+ hParams .SetVector (2 , {0.0 , 0.0 , 0.0 });
231+ }
232+
233+ if (! hParams .IsNull (3 ))
234+ {
235+ hParams .SetObjectVar (3 , 0 , ObjectValueType_Int , area );
236+ }
237+
238+ hReturn .Value = true ;
239+ return MRES_ChangedOverride ;
240+ }
241+
242+ // GetPlayerSpawnPosition(SurvivorCharacterType,Vector *,QAngle *,TerrorNavArea **)
162243MRESReturn DTR_GetPlayerSpawnPosition_Post (DHookReturn hReturn , DHookParam hParams )
163244{
164245 if (! g_bInComputeFlowDistances )
@@ -207,7 +288,7 @@ void NextFrame_ComputeFlowDistance()
207288
208289 if (g_SavedEscapeRouteAreas .Length != TheEscapeRoute .m_nMainPathAreaCount )
209290 {
210- PrintToServer (" Count mismatches (was %d , now %d )" , PLUGIN_TAG , g_SavedEscapeRouteAreas .Length , TheEscapeRoute .m_nMainPathAreaCount );
291+ PrintToServer (" Count mismatches (was %d , now %d )" , g_SavedEscapeRouteAreas .Length , TheEscapeRoute .m_nMainPathAreaCount );
211292 fullyMatched = false ;
212293 }
213294 else
@@ -217,7 +298,7 @@ void NextFrame_ComputeFlowDistance()
217298 TerrorNavArea area = TheEscapeRoute .GetMainPathArea (i );
218299 if (area .GetID () != g_SavedEscapeRouteAreas .Get (i ))
219300 {
220- PrintToServer (" Area @ %d mismatches (was #%d , now #%d )" , PLUGIN_TAG , i , g_SavedEscapeRouteAreas .Get (i ), area .GetID ());
301+ PrintToServer (" Area @ %d mismatches (was #%d , now #%d )" , i , g_SavedEscapeRouteAreas .Get (i ), area .GetID ());
221302 fullyMatched = false ;
222303 break ;
223304 }
0 commit comments