11#include < amxmodx>
2- #include < hamsandwich>
32#include < fakemeta>
3+ #include < hamsandwich>
44#include < reapi>
55
66# define PLUGIN " [API] Player Model"
77# define VERSION " 1.0.0"
88# define AUTHOR " Hedgehog Fog"
99
1010# define MAX_SEQUENCES 101
11+ # define USER_NOT_CONNECTED_MESSAGE " User %d is not connected"
1112
12- new g_iszModelClassname ;
13+ new g_iszSubModelClassname ;
1314
1415new g_rgszDefaultPlayerModel[MAX_PLAYERS + 1 ][32 ];
1516new g_rgszCurrentPlayerModel[MAX_PLAYERS + 1 ][256 ];
1617new g_rgszCustomPlayerModel[MAX_PLAYERS + 1 ][256 ];
1718new g_rgiPlayerAnimationIndex[MAX_PLAYERS + 1 ];
19+ new g_pPlayerSubModel[MAX_PLAYERS + 1 ];
1820new bool: g_rgbPlayerUseCustomModel[MAX_PLAYERS + 1 ];
1921
2022new Trie: g_itPlayerSequenceModelIndexes = Invalid_Trie;
2123new Trie: g_itPlayerSequences = Invalid_Trie;
22- new g_pPlayerSubModel[MAX_PLAYERS + 1 ];
23-
24- new gmsgClCorpse;
2524
2625public plugin_precache () {
27- g_iszModelClassname = engfunc (EngFunc_AllocString, " info_target" );
26+ g_iszSubModelClassname = engfunc (EngFunc_AllocString, " info_target" );
2827 g_itPlayerSequenceModelIndexes = TrieCreate ();
2928 g_itPlayerSequences = TrieCreate ();
3029}
3130
3231public plugin_init () {
3332 register_plugin (PLUGIN, VERSION, AUTHOR);
3433
35- gmsgClCorpse = get_user_msgid (" ClCorpse" );
36-
3734 register_forward (FM_SetClientKeyValue, " FMHook_SetClientKeyValue" );
3835
3936 RegisterHamPlayer (Ham_Spawn, " HamHook_Player_Spawn_Post" , .Post = 1 );
4037 RegisterHamPlayer (Ham_Player_PostThink, " HamHook_Player_PostThink_Post" , .Post = 1 );
4138
4239 RegisterHookChain (RG_CBasePlayer_SetAnimation, " HC_Player_SetAnimation" );
4340
44- register_message (gmsgClCorpse , " Message_ClCorpse" );
41+ register_message (get_user_msgid ( " ClCorpse " ) , " Message_ClCorpse" );
4542}
4643
4744public plugin_natives () {
@@ -57,19 +54,43 @@ public plugin_natives() {
5754 register_native (" PlayerModel_PrecacheAnimation" , " Native_PrecacheAnimation" );
5855}
5956
57+ public plugin_end () {
58+ TrieDestroy (g_itPlayerSequenceModelIndexes);
59+ TrieDestroy (g_itPlayerSequences);
60+ }
61+
62+ // ANCHOR: Natives
63+
6064public Native_GetPlayerModel (iPluginId, iArgc) {
6165 new pPlayer = get_param (1 );
66+
67+ if (! is_user_connected (pPlayer)) {
68+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
69+ return ;
70+ }
71+
6272 set_string (2 , g_rgszCustomPlayerModel[pPlayer], get_param (3 ));
6373}
6474
6575public Native_GetCurrentPlayerModel (iPluginId, iArgc) {
6676 new pPlayer = get_param (1 );
77+
78+ if (! is_user_connected (pPlayer)) {
79+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
80+ return ;
81+ }
82+
6783 set_string (2 , g_rgszCurrentPlayerModel[pPlayer], get_param (3 ));
6884}
6985
7086public Native_GetPlayerEntity (iPluginId, iArgc) {
7187 new pPlayer = get_param (1 );
7288
89+ if (! is_user_connected (pPlayer)) {
90+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
91+ return 0 ;
92+ }
93+
7394 if (g_pPlayerSubModel[pPlayer] && @PlayerSubModel_IsActive (g_pPlayerSubModel[pPlayer])) {
7495 return g_pPlayerSubModel[pPlayer];
7596 }
@@ -80,27 +101,55 @@ public Native_GetPlayerEntity(iPluginId, iArgc) {
80101public bool: Native_HasCustomPlayerModel (iPluginId, iArgc) {
81102 new pPlayer = get_param (1 );
82103
104+ if (! is_user_connected (pPlayer)) {
105+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
106+ return false ;
107+ }
108+
83109 return g_rgbPlayerUseCustomModel[pPlayer];
84110}
85111
86112public Native_SetPlayerModel (iPluginId, iArgc) {
87113 new pPlayer = get_param (1 );
114+
115+ if (! is_user_connected (pPlayer)) {
116+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
117+ return ;
118+ }
119+
88120 get_string (2 , g_rgszCustomPlayerModel[pPlayer], charsmax (g_rgszCustomPlayerModel[]));
89121}
90122
91123public Native_ResetPlayerModel (iPluginId, iArgc) {
92124 new pPlayer = get_param (1 );
125+
126+ if (! is_user_connected (pPlayer)) {
127+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
128+ return ;
129+ }
130+
93131 @Player_ResetModel (pPlayer);
94132}
95133
96134public Native_UpdatePlayerModel (iPluginId, iArgc) {
97135 new pPlayer = get_param (1 );
136+
137+ if (! is_user_connected (pPlayer)) {
138+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
139+ return ;
140+ }
141+
98142 @Player_UpdateCurrentModel (pPlayer);
99143}
100144
101145public Native_SetPlayerSequence (iPluginId, iArgc) {
102146 new pPlayer = get_param (1 );
103147
148+ if (! is_user_connected (pPlayer)) {
149+ log_error (AMX_ERR_NATIVE, USER_NOT_CONNECTED_MESSAGE, pPlayer);
150+ return 0 ;
151+ }
152+
104153 static szSequence[MAX_RESOURCE_PATH_LENGTH];
105154 get_string (2 , szSequence, charsmax (szSequence));
106155
@@ -113,6 +162,8 @@ public Native_PrecacheAnimation(iPluginId, iArgc) {
113162 return PrecachePlayerAnimation (szAnimation);
114163}
115164
165+ // ANCHOR: Hooks and Forwards
166+
116167public client_connect (pPlayer) {
117168 copy (g_rgszCustomPlayerModel[pPlayer], charsmax (g_rgszCustomPlayerModel[]), NULL_STRING);
118169 copy (g_rgszDefaultPlayerModel[pPlayer], charsmax (g_rgszDefaultPlayerModel[]), NULL_STRING);
@@ -128,19 +179,24 @@ public client_disconnected(pPlayer) {
128179 }
129180}
130181
131- public Message_ClCorpse (iMsgId, iMsgDest, pPlayer) {
132- new pTargetPlayer = get_msg_arg_int (12 );
133- if (g_rgbPlayerUseCustomModel[pTargetPlayer] || g_rgiPlayerAnimationIndex[pTargetPlayer]) {
134- set_msg_arg_string (1 , g_rgszCurrentPlayerModel[pTargetPlayer]);
182+ public FMHook_SetClientKeyValue (pPlayer, const szInfoBuffer[], const szKey[], const szValue[]) {
183+ if (equal (szKey, " model" )) {
184+ copy (g_rgszDefaultPlayerModel[pPlayer], charsmax (g_rgszDefaultPlayerModel[]), szValue);
185+
186+ if (! equal (g_rgszCurrentPlayerModel[pPlayer], NULL_STRING)) {
187+ return FMRES_SUPERCEDE;
188+ }
189+
190+ return FMRES_HANDLED;
135191 }
192+
193+ return FMRES_IGNORED;
136194}
137195
138196public HamHook_Player_Spawn_Post (pPlayer) {
139- if (! g_pPlayerSubModel[pPlayer]) {
140- g_pPlayerSubModel[pPlayer] = @PlayerSubModel_Create (pPlayer);
141- }
142-
143197 @Player_UpdateCurrentModel (pPlayer);
198+
199+ return HAM_HANDLED;
144200}
145201
146202public HamHook_Player_PostThink_Post (pPlayer) {
@@ -151,25 +207,20 @@ public HamHook_Player_PostThink_Post(pPlayer) {
151207 return HAM_HANDLED;
152208}
153209
154- public FMHook_SetClientKeyValue (pPlayer, const szInfoBuffer[], const szKey[], const szValue[]) {
155- if (equal (szKey, " model" )) {
156- copy (g_rgszDefaultPlayerModel[pPlayer], charsmax (g_rgszDefaultPlayerModel[]), szValue);
157-
158- if (! equal (g_rgszCurrentPlayerModel[pPlayer], NULL_STRING)) {
159- return FMRES_SUPERCEDE;
160- }
210+ public HC_Player_SetAnimation (pPlayer) {
211+ @Player_UpdateAnimationModel (pPlayer);
212+ }
161213
162- return FMRES_IGNORED;
214+ public Message_ClCorpse (iMsgId, iMsgDest, pPlayer) {
215+ new pTargetPlayer = get_msg_arg_int (12 );
216+ if (@Player_ShouldUseCurrentModel (pTargetPlayer)) {
217+ set_msg_arg_string (1 , g_rgszCurrentPlayerModel[pTargetPlayer]);
163218 }
164-
165- return FMRES_IGNORED;
166219}
167220
168- public HC_Player_SetAnimation (pPlayer) {
169- @Player_UpdateAnimationModel (pPlayer);
170- }
221+ // ANCHOR: Methods
171222
172- public @Player_UpdateAnimationModel (this) {
223+ @Player_UpdateAnimationModel (this) {
173224 static szAnimExt[32 ];
174225 get_member (this, m_szAnimExtention, szAnimExt, charsmax (szAnimExt));
175226
@@ -180,34 +231,44 @@ public @Player_UpdateAnimationModel(this) {
180231 }
181232}
182233
183- public @Player_UpdateCurrentModel (this) {
234+ @Player_UpdateCurrentModel (this) {
184235 new bool: bUsedCustom = g_rgbPlayerUseCustomModel[this];
185236
186237 g_rgbPlayerUseCustomModel[this] = ! equal (g_rgszCustomPlayerModel[this], NULL_STRING);
187238
188239 if (g_rgbPlayerUseCustomModel[this]) {
189240 copy (g_rgszCurrentPlayerModel[this], charsmax (g_rgszCurrentPlayerModel[]), g_rgszCustomPlayerModel[this]);
190- } else {
191- if (! equal (g_rgszDefaultPlayerModel[this], NULL_STRING)) {
192- format (g_rgszCurrentPlayerModel[this], charsmax (g_rgszCurrentPlayerModel[]), " models/player/%s /%s .mdl" , g_rgszDefaultPlayerModel[this], g_rgszDefaultPlayerModel[this]);
193- }
241+ } else if (! equal (g_rgszDefaultPlayerModel[this], NULL_STRING)) {
242+ format (g_rgszCurrentPlayerModel[this], charsmax (g_rgszCurrentPlayerModel[]), " models/player/%s /%s .mdl" , g_rgszDefaultPlayerModel[this], g_rgszDefaultPlayerModel[this]);
194243 }
195244
196245 @Player_UpdateModel (this, bUsedCustom && ! g_rgbPlayerUseCustomModel[this]);
197246}
198247
199- public @Player_UpdateModel (this, bool: bForce) {
200- if (bForce || (g_rgbPlayerUseCustomModel[this] || g_rgiPlayerAnimationIndex[this])) {
248+ @Player_UpdateModel (this, bool: bForceUpdate) {
249+ new iSubModelModelIndex = 0 ;
250+
251+ if (bForceUpdate || @Player_ShouldUseCurrentModel (this)) {
201252 new iAnimationIndex = g_rgiPlayerAnimationIndex[this];
202253 new iModelIndex = engfunc (EngFunc_ModelIndex, g_rgszCurrentPlayerModel[this]);
203254 @Player_SetModelIndex (this, iAnimationIndex ? iAnimationIndex : iModelIndex);
204- set_pev (g_pPlayerSubModel[this], pev_modelindex, iAnimationIndex ? iModelIndex : 0 );
205- } else {
206- set_pev (g_pPlayerSubModel[this], pev_modelindex, 0 );
255+ iSubModelModelIndex = iAnimationIndex ? iModelIndex : 0 ;
256+ }
257+
258+ if (iSubModelModelIndex && ! g_pPlayerSubModel[this]) {
259+ g_pPlayerSubModel[this] = @PlayerSubModel_Create (this);
260+ }
261+
262+ if (g_pPlayerSubModel[this]) {
263+ set_pev (g_pPlayerSubModel[this], pev_modelindex, iSubModelModelIndex);
207264 }
208265}
209266
210- public @Player_ResetModel (this) {
267+ bool: @Player_ShouldUseCurrentModel (this) {
268+ return g_rgbPlayerUseCustomModel[this] || g_rgiPlayerAnimationIndex[this];
269+ }
270+
271+ @Player_ResetModel (this) {
211272 if (equal (g_rgszDefaultPlayerModel[this], NULL_STRING)) {
212273 return ;
213274 }
@@ -219,13 +280,13 @@ public @Player_ResetModel(this) {
219280 @Player_UpdateCurrentModel (this);
220281}
221282
222- public @Player_SetModelIndex (this, iModelIndex) {
283+ @Player_SetModelIndex (this, iModelIndex) {
223284 set_user_info (this, " model" , " " );
224285 set_pev (this, pev_modelindex, iModelIndex);
225286 set_member (this, m_modelIndexPlayer, iModelIndex);
226287}
227288
228- public @Player_SetSequence (this, const szSequence[]) {
289+ @Player_SetSequence (this, const szSequence[]) {
229290 new iAnimationIndex = GetAnimationIndexBySequence (szSequence);
230291 if (! iAnimationIndex) {
231292 return - 1 ;
@@ -239,22 +300,22 @@ public @Player_SetSequence(this, const szSequence[]) {
239300 return iSequence;
240301}
241302
242- public @PlayerSubModel_Create (pPlayer) {
243- new this = engfunc (EngFunc_CreateNamedEntity, g_iszModelClassname );
303+ @PlayerSubModel_Create (pPlayer) {
304+ new this = engfunc (EngFunc_CreateNamedEntity, g_iszSubModelClassname );
244305 set_pev (this, pev_movetype, MOVETYPE_FOLLOW);
245306 set_pev (this, pev_aiment, pPlayer);
246307 set_pev (this, pev_owner, pPlayer);
247308
248309 return this;
249310}
250311
251- public @PlayerSubModel_Destroy (this) {
312+ @PlayerSubModel_Destroy (this) {
252313 set_pev (this, pev_modelindex, 0 );
253314 set_pev (this, pev_flags, pev (this, pev_flags) | FL_KILLME);
254315 dllfunc (DLLFunc_Think, this);
255316}
256317
257- public @PlayerSubModel_Think (this) {
318+ @PlayerSubModel_Think (this) {
258319 if (! @PlayerSubModel_IsActive (this)) {
259320 set_entvar (this, var_effects, get_entvar (this, var_effects) | EF_NODRAW);
260321 return ;
@@ -275,10 +336,12 @@ public @PlayerSubModel_Think(this) {
275336 set_entvar (this, var_rendercolor, rgflColor);
276337}
277338
278- public @PlayerSubModel_IsActive (this) {
339+ @PlayerSubModel_IsActive (this) {
279340 return (pev (this, pev_modelindex) > 0 );
280341}
281342
343+ // ANCHOR: Functions
344+
282345GetAnimationIndexByAnimExt (const szAnimExt[]) {
283346 static szSequence[32 ];
284347 format (szSequence, charsmax (szSequence), " ref_aim_%s " , szAnimExt);
0 commit comments