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
10+ # define ERROR_MSG_NOT_CONNECTED " User %d is not connected"
11+
1012# define MAX_SEQUENCES 101
1113
12- new g_iszModelClassname ;
14+ new g_iszSubModelClassname ;
1315
1416new g_rgszDefaultPlayerModel[MAX_PLAYERS + 1 ][32 ];
1517new g_rgszCurrentPlayerModel[MAX_PLAYERS + 1 ][256 ];
1618new g_rgszCustomPlayerModel[MAX_PLAYERS + 1 ][256 ];
1719new g_rgiPlayerAnimationIndex[MAX_PLAYERS + 1 ];
20+ new g_pPlayerSubModel[MAX_PLAYERS + 1 ];
1821new bool: g_rgbPlayerUseCustomModel[MAX_PLAYERS + 1 ];
1922
2023new Trie: g_itPlayerSequenceModelIndexes = Invalid_Trie;
2124new Trie: g_itPlayerSequences = Invalid_Trie;
22- new g_pPlayerSubModel[MAX_PLAYERS + 1 ];
23-
24- new gmsgClCorpse;
2525
2626public plugin_precache () {
27- g_iszModelClassname = engfunc (EngFunc_AllocString, " info_target" );
27+ g_iszSubModelClassname = engfunc (EngFunc_AllocString, " info_target" );
2828 g_itPlayerSequenceModelIndexes = TrieCreate ();
2929 g_itPlayerSequences = TrieCreate ();
3030}
3131
3232public plugin_init () {
3333 register_plugin (PLUGIN, VERSION, AUTHOR);
3434
35- gmsgClCorpse = get_user_msgid (" ClCorpse" );
36-
3735 register_forward (FM_SetClientKeyValue, " FMHook_SetClientKeyValue" );
3836
3937 RegisterHamPlayer (Ham_Spawn, " HamHook_Player_Spawn_Post" , .Post = 1 );
4038 RegisterHamPlayer (Ham_Player_PostThink, " HamHook_Player_PostThink_Post" , .Post = 1 );
4139
4240 RegisterHookChain (RG_CBasePlayer_SetAnimation, " HC_Player_SetAnimation" );
4341
44- register_message (gmsgClCorpse , " Message_ClCorpse" );
42+ register_message (get_user_msgid ( " ClCorpse " ) , " Message_ClCorpse" );
4543}
4644
4745public plugin_natives () {
@@ -57,19 +55,43 @@ public plugin_natives() {
5755 register_native (" PlayerModel_PrecacheAnimation" , " Native_PrecacheAnimation" );
5856}
5957
58+ public plugin_end () {
59+ TrieDestroy (g_itPlayerSequenceModelIndexes);
60+ TrieDestroy (g_itPlayerSequences);
61+ }
62+
63+ // ANCHOR: Natives
64+
6065public Native_GetPlayerModel (iPluginId, iArgc) {
6166 new pPlayer = get_param (1 );
67+
68+ if (! is_user_connected (pPlayer)) {
69+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
70+ return ;
71+ }
72+
6273 set_string (2 , g_rgszCustomPlayerModel[pPlayer], get_param (3 ));
6374}
6475
6576public Native_GetCurrentPlayerModel (iPluginId, iArgc) {
6677 new pPlayer = get_param (1 );
78+
79+ if (! is_user_connected (pPlayer)) {
80+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
81+ return ;
82+ }
83+
6784 set_string (2 , g_rgszCurrentPlayerModel[pPlayer], get_param (3 ));
6885}
6986
7087public Native_GetPlayerEntity (iPluginId, iArgc) {
7188 new pPlayer = get_param (1 );
7289
90+ if (! is_user_connected (pPlayer)) {
91+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
92+ return 0 ;
93+ }
94+
7395 if (g_pPlayerSubModel[pPlayer] && @PlayerSubModel_IsActive (g_pPlayerSubModel[pPlayer])) {
7496 return g_pPlayerSubModel[pPlayer];
7597 }
@@ -80,27 +102,55 @@ public Native_GetPlayerEntity(iPluginId, iArgc) {
80102public bool: Native_HasCustomPlayerModel (iPluginId, iArgc) {
81103 new pPlayer = get_param (1 );
82104
105+ if (! is_user_connected (pPlayer)) {
106+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
107+ return false ;
108+ }
109+
83110 return g_rgbPlayerUseCustomModel[pPlayer];
84111}
85112
86113public Native_SetPlayerModel (iPluginId, iArgc) {
87114 new pPlayer = get_param (1 );
115+
116+ if (! is_user_connected (pPlayer)) {
117+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
118+ return ;
119+ }
120+
88121 get_string (2 , g_rgszCustomPlayerModel[pPlayer], charsmax (g_rgszCustomPlayerModel[]));
89122}
90123
91124public Native_ResetPlayerModel (iPluginId, iArgc) {
92125 new pPlayer = get_param (1 );
126+
127+ if (! is_user_connected (pPlayer)) {
128+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
129+ return ;
130+ }
131+
93132 @Player_ResetModel (pPlayer);
94133}
95134
96135public Native_UpdatePlayerModel (iPluginId, iArgc) {
97136 new pPlayer = get_param (1 );
137+
138+ if (! is_user_connected (pPlayer)) {
139+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
140+ return ;
141+ }
142+
98143 @Player_UpdateCurrentModel (pPlayer);
99144}
100145
101146public Native_SetPlayerSequence (iPluginId, iArgc) {
102147 new pPlayer = get_param (1 );
103148
149+ if (! is_user_connected (pPlayer)) {
150+ log_error (AMX_ERR_NATIVE, ERROR_MSG_NOT_CONNECTED, pPlayer);
151+ return 0 ;
152+ }
153+
104154 static szSequence[MAX_RESOURCE_PATH_LENGTH];
105155 get_string (2 , szSequence, charsmax (szSequence));
106156
@@ -113,6 +163,8 @@ public Native_PrecacheAnimation(iPluginId, iArgc) {
113163 return PrecachePlayerAnimation (szAnimation);
114164}
115165
166+ // ANCHOR: Hooks and Forwards
167+
116168public client_connect (pPlayer) {
117169 copy (g_rgszCustomPlayerModel[pPlayer], charsmax (g_rgszCustomPlayerModel[]), NULL_STRING);
118170 copy (g_rgszDefaultPlayerModel[pPlayer], charsmax (g_rgszDefaultPlayerModel[]), NULL_STRING);
@@ -128,19 +180,24 @@ public client_disconnected(pPlayer) {
128180 }
129181}
130182
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]);
183+ public FMHook_SetClientKeyValue (pPlayer, const szInfoBuffer[], const szKey[], const szValue[]) {
184+ if (equal (szKey, " model" )) {
185+ copy (g_rgszDefaultPlayerModel[pPlayer], charsmax (g_rgszDefaultPlayerModel[]), szValue);
186+
187+ if (! equal (g_rgszCurrentPlayerModel[pPlayer], NULL_STRING)) {
188+ return FMRES_SUPERCEDE;
189+ }
190+
191+ return FMRES_HANDLED;
135192 }
193+
194+ return FMRES_IGNORED;
136195}
137196
138197public HamHook_Player_Spawn_Post (pPlayer) {
139- if (! g_pPlayerSubModel[pPlayer]) {
140- g_pPlayerSubModel[pPlayer] = @PlayerSubModel_Create (pPlayer);
141- }
142-
143198 @Player_UpdateCurrentModel (pPlayer);
199+
200+ return HAM_HANDLED;
144201}
145202
146203public HamHook_Player_PostThink_Post (pPlayer) {
@@ -151,25 +208,20 @@ public HamHook_Player_PostThink_Post(pPlayer) {
151208 return HAM_HANDLED;
152209}
153210
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- }
211+ public HC_Player_SetAnimation (pPlayer) {
212+ @Player_UpdateAnimationModel (pPlayer);
213+ }
161214
162- return FMRES_IGNORED;
215+ public Message_ClCorpse (iMsgId, iMsgDest, pPlayer) {
216+ new pTargetPlayer = get_msg_arg_int (12 );
217+ if (@Player_ShouldUseCurrentModel (pTargetPlayer)) {
218+ set_msg_arg_string (1 , g_rgszCurrentPlayerModel[pTargetPlayer]);
163219 }
164-
165- return FMRES_IGNORED;
166220}
167221
168- public HC_Player_SetAnimation (pPlayer) {
169- @Player_UpdateAnimationModel (pPlayer);
170- }
222+ // ANCHOR: Methods
171223
172- public @Player_UpdateAnimationModel (this) {
224+ @Player_UpdateAnimationModel (this) {
173225 static szAnimExt[32 ];
174226 get_member (this, m_szAnimExtention, szAnimExt, charsmax (szAnimExt));
175227
@@ -180,34 +232,44 @@ public @Player_UpdateAnimationModel(this) {
180232 }
181233}
182234
183- public @Player_UpdateCurrentModel (this) {
235+ @Player_UpdateCurrentModel (this) {
184236 new bool: bUsedCustom = g_rgbPlayerUseCustomModel[this];
185237
186238 g_rgbPlayerUseCustomModel[this] = ! equal (g_rgszCustomPlayerModel[this], NULL_STRING);
187239
188240 if (g_rgbPlayerUseCustomModel[this]) {
189241 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- }
242+ } else if (! equal (g_rgszDefaultPlayerModel[this], NULL_STRING)) {
243+ format (g_rgszCurrentPlayerModel[this], charsmax (g_rgszCurrentPlayerModel[]), " models/player/%s /%s .mdl" , g_rgszDefaultPlayerModel[this], g_rgszDefaultPlayerModel[this]);
194244 }
195245
196246 @Player_UpdateModel (this, bUsedCustom && ! g_rgbPlayerUseCustomModel[this]);
197247}
198248
199- public @Player_UpdateModel (this, bool: bForce) {
200- if (bForce || (g_rgbPlayerUseCustomModel[this] || g_rgiPlayerAnimationIndex[this])) {
249+ @Player_UpdateModel (this, bool: bForceUpdate) {
250+ new iSubModelModelIndex = 0 ;
251+
252+ if (bForceUpdate || @Player_ShouldUseCurrentModel (this)) {
201253 new iAnimationIndex = g_rgiPlayerAnimationIndex[this];
202254 new iModelIndex = engfunc (EngFunc_ModelIndex, g_rgszCurrentPlayerModel[this]);
203255 @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 );
256+ iSubModelModelIndex = iAnimationIndex ? iModelIndex : 0 ;
257+ }
258+
259+ if (iSubModelModelIndex && ! g_pPlayerSubModel[this]) {
260+ g_pPlayerSubModel[this] = @PlayerSubModel_Create (this);
261+ }
262+
263+ if (g_pPlayerSubModel[this]) {
264+ set_pev (g_pPlayerSubModel[this], pev_modelindex, iSubModelModelIndex);
207265 }
208266}
209267
210- public @Player_ResetModel (this) {
268+ bool: @Player_ShouldUseCurrentModel (this) {
269+ return g_rgbPlayerUseCustomModel[this] || g_rgiPlayerAnimationIndex[this];
270+ }
271+
272+ @Player_ResetModel (this) {
211273 if (equal (g_rgszDefaultPlayerModel[this], NULL_STRING)) {
212274 return ;
213275 }
@@ -219,13 +281,13 @@ public @Player_ResetModel(this) {
219281 @Player_UpdateCurrentModel (this);
220282}
221283
222- public @Player_SetModelIndex (this, iModelIndex) {
284+ @Player_SetModelIndex (this, iModelIndex) {
223285 set_user_info (this, " model" , " " );
224286 set_pev (this, pev_modelindex, iModelIndex);
225287 set_member (this, m_modelIndexPlayer, iModelIndex);
226288}
227289
228- public @Player_SetSequence (this, const szSequence[]) {
290+ @Player_SetSequence (this, const szSequence[]) {
229291 new iAnimationIndex = GetAnimationIndexBySequence (szSequence);
230292 if (! iAnimationIndex) {
231293 return - 1 ;
@@ -239,22 +301,22 @@ public @Player_SetSequence(this, const szSequence[]) {
239301 return iSequence;
240302}
241303
242- public @PlayerSubModel_Create (pPlayer) {
243- new this = engfunc (EngFunc_CreateNamedEntity, g_iszModelClassname );
304+ @PlayerSubModel_Create (pPlayer) {
305+ new this = engfunc (EngFunc_CreateNamedEntity, g_iszSubModelClassname );
244306 set_pev (this, pev_movetype, MOVETYPE_FOLLOW);
245307 set_pev (this, pev_aiment, pPlayer);
246308 set_pev (this, pev_owner, pPlayer);
247309
248310 return this;
249311}
250312
251- public @PlayerSubModel_Destroy (this) {
313+ @PlayerSubModel_Destroy (this) {
252314 set_pev (this, pev_modelindex, 0 );
253315 set_pev (this, pev_flags, pev (this, pev_flags) | FL_KILLME);
254316 dllfunc (DLLFunc_Think, this);
255317}
256318
257- public @PlayerSubModel_Think (this) {
319+ @PlayerSubModel_Think (this) {
258320 if (! @PlayerSubModel_IsActive (this)) {
259321 set_entvar (this, var_effects, get_entvar (this, var_effects) | EF_NODRAW);
260322 return ;
@@ -275,10 +337,12 @@ public @PlayerSubModel_Think(this) {
275337 set_entvar (this, var_rendercolor, rgflColor);
276338}
277339
278- public @PlayerSubModel_IsActive (this) {
340+ @PlayerSubModel_IsActive (this) {
279341 return (pev (this, pev_modelindex) > 0 );
280342}
281343
344+ // ANCHOR: Functions
345+
282346GetAnimationIndexByAnimExt (const szAnimExt[]) {
283347 static szSequence[32 ];
284348 format (szSequence, charsmax (szSequence), " ref_aim_%s " , szAnimExt);
0 commit comments