11#include < amxmodx>
22#include < hamsandwich>
3- #include < reapi>
43#include < fakemeta>
4+ #include < reapi>
55
66# define PLUGIN " [API] Player Model"
7- # define VERSION " 0.9 .0"
7+ # define VERSION " 1.0 .0"
88# define AUTHOR " Hedgehog Fog"
99
10- new g_rgszPlayerModel[MAX_PLAYERS + 1 ][32 ];
10+ # define MAX_SEQUENCES 101
11+
12+ new g_rgszDefaultPlayerModel[MAX_PLAYERS + 1 ][32 ];
13+ new g_rgszCurrentPlayerModel[MAX_PLAYERS + 1 ][256 ];
1114new g_rgszCustomPlayerModel[MAX_PLAYERS + 1 ][256 ];
15+ new g_rgiPlayerAnimationIndex[MAX_PLAYERS + 1 ];
16+
17+ new Trie: g_itPlayerSequenceModelIndexes = Invalid_Trie;
18+ new Trie: g_itPlayerSequences = Invalid_Trie;
19+ new g_pPlayerModel[MAX_PLAYERS + 1 ];
20+
21+ new gmsgClCorpse;
22+
23+ public plugin_precache () {
24+ g_itPlayerSequenceModelIndexes = TrieCreate ();
25+ g_itPlayerSequences = TrieCreate ();
26+ }
1227
1328public plugin_init () {
1429 register_plugin (PLUGIN, VERSION, AUTHOR);
1530
16- RegisterHamPlayer (Ham_Spawn, " HamHook_Player_Spawn_Post " , .Post = 1 );
31+ gmsgClCorpse = get_user_msgid ( " ClCorpse " );
1732
1833 register_forward (FM_SetClientKeyValue, " FMHook_SetClientKeyValue" );
34+
35+ RegisterHamPlayer (Ham_Spawn, " HamHook_Player_Spawn_Post" , .Post = 1 );
36+ RegisterHamPlayer (Ham_Player_PostThink, " HamHook_Player_PostThink_Post" , .Post = 1 );
37+
38+ RegisterHookChain (RG_CBasePlayer_SetAnimation, " HC_Player_SetAnimation" );
39+
40+ register_message (gmsgClCorpse, " Message_ClCorpse" );
1941}
2042
2143public plugin_natives () {
@@ -24,11 +46,12 @@ public plugin_natives() {
2446 register_native (" PlayerModel_Set" , " Native_SetPlayerModel" );
2547 register_native (" PlayerModel_Reset" , " Native_ResetPlayerModel" );
2648 register_native (" PlayerModel_Update" , " Native_UpdatePlayerModel" );
49+ register_native (" PlayerModel_PrecacheAnimation" , " Native_PrecacheAnimation" );
50+ register_native (" PlayerModel_SetSequence" , " Native_SetPlayerSequence" );
2751}
2852
2953public Native_GetPlayerModel (iPluginId, iArgc) {
3054 new pPlayer = get_param (1 );
31-
3255 set_string (2 , g_rgszCustomPlayerModel[pPlayer], get_param (3 ));
3356}
3457
@@ -44,23 +67,67 @@ public Native_ResetPlayerModel(iPluginId, iArgc) {
4467
4568public Native_UpdatePlayerModel (iPluginId, iArgc) {
4669 new pPlayer = get_param (1 );
47- @Player_UpdateModel (pPlayer);
70+ @Player_UpdateCurrentModel (pPlayer);
71+ }
72+
73+ public Native_PrecacheAnimation (iPluginId, iArgc) {
74+ static szAnimation[MAX_RESOURCE_PATH_LENGTH];
75+ get_string (1 , szAnimation, charsmax (szAnimation));
76+ PrecachePlayerAnimation (szAnimation);
77+ }
78+
79+ public Native_SetPlayerSequence (iPluginId, iArgc) {
80+ new pPlayer = get_param (1 );
81+
82+ static szSequence[MAX_RESOURCE_PATH_LENGTH];
83+ get_string (2 , szSequence, charsmax (szSequence));
84+
85+ return @Player_SetSequence (pPlayer, szSequence);
4886}
4987
5088public client_connect (pPlayer) {
5189 copy (g_rgszCustomPlayerModel[pPlayer], charsmax (g_rgszCustomPlayerModel[]), NULL_STRING);
52- copy (g_rgszPlayerModel[pPlayer], charsmax (g_rgszPlayerModel[]), NULL_STRING);
90+ copy (g_rgszDefaultPlayerModel[pPlayer], charsmax (g_rgszDefaultPlayerModel[]), NULL_STRING);
91+ }
92+
93+ public Message_ClCorpse (iMsgId, iMsgDest, pPlayer) {
94+ new pTargetPlayer = get_msg_arg_int (12 );
95+ set_msg_arg_string (1 , g_rgszCurrentPlayerModel[pTargetPlayer]);
5396}
5497
5598public HamHook_Player_Spawn_Post (pPlayer) {
56- @Player_UpdateModel (pPlayer);
99+ if (! g_pPlayerModel[pPlayer]) {
100+ new pPlayerModel = engfunc (EngFunc_CreateNamedEntity, engfunc (EngFunc_AllocString, " info_target" ));
101+ set_pev (pPlayerModel, pev_movetype, MOVETYPE_FOLLOW);
102+ set_pev (pPlayerModel, pev_aiment, pPlayer);
103+ g_pPlayerModel[pPlayer] = pPlayerModel;
104+ }
105+
106+ @Player_UpdateCurrentModel (pPlayer);
107+ }
108+
109+ public HamHook_Player_PostThink_Post (pPlayer) {
110+ if (g_pPlayerModel[pPlayer]) {
111+ set_entvar (g_pPlayerModel[pPlayer], var_skin, get_entvar (pPlayer, var_skin));
112+ set_entvar (g_pPlayerModel[pPlayer], var_body, get_entvar (pPlayer, var_body));
113+ set_entvar (g_pPlayerModel[pPlayer], var_colormap, get_entvar (pPlayer, var_colormap));
114+ set_entvar (g_pPlayerModel[pPlayer], var_rendermode, get_entvar (pPlayer, var_rendermode));
115+ set_entvar (g_pPlayerModel[pPlayer], var_renderfx, get_entvar (pPlayer, var_renderfx));
116+ set_entvar (g_pPlayerModel[pPlayer], var_renderamt, get_entvar (pPlayer, var_renderamt));
117+
118+ static rgflColor[3 ];
119+ get_entvar (pPlayer, var_rendercolor, rgflColor);
120+ set_entvar (g_pPlayerModel[pPlayer], var_rendercolor, rgflColor);
121+ }
122+
123+ return HAM_HANDLED;
57124}
58125
59126public FMHook_SetClientKeyValue (pPlayer, const szInfoBuffer[], const szKey[], const szValue[]) {
60127 if (equal (szKey, " model" )) {
61- copy (g_rgszPlayerModel [pPlayer], charsmax (g_rgszPlayerModel []), szValue);
128+ copy (g_rgszDefaultPlayerModel [pPlayer], charsmax (g_rgszDefaultPlayerModel []), szValue);
62129
63- if (! equal (g_rgszCustomPlayerModel [pPlayer], NULL_STRING)) {
130+ if (! equal (g_rgszCurrentPlayerModel [pPlayer], NULL_STRING)) {
64131 return FMRES_SUPERCEDE;
65132 }
66133
@@ -70,28 +137,142 @@ public FMHook_SetClientKeyValue(pPlayer, const szInfoBuffer[], const szKey[], co
70137 return FMRES_IGNORED;
71138}
72139
73- public @Player_UpdateModel (this) {
74- if (! equal (g_rgszCustomPlayerModel[this], NULL_STRING)) {
75- new iModelIndex = engfunc (EngFunc_ModelIndex, g_rgszCustomPlayerModel[this]);
76- set_user_info (this, " model" , " " );
77- set_pev (this, pev_modelindex, iModelIndex);
78- set_member (this, m_modelIndexPlayer, iModelIndex);
140+ public HC_Player_SetAnimation (pPlayer) {
141+ @Player_UpdateAnimationModel (pPlayer);
142+ }
143+
144+ public @Player_UpdateAnimationModel (this) {
145+ static szAnimExt[32 ];
146+ get_member (this, m_szAnimExtention, szAnimExt, charsmax (szAnimExt));
147+
148+ new iAnimationIndex = is_user_alive (this) ? GetAnimationIndexByAnimExt (szAnimExt) : 0 ;
149+ if (iAnimationIndex != g_rgiPlayerAnimationIndex[this]) {
150+ g_rgiPlayerAnimationIndex[this] = iAnimationIndex;
151+ @Player_UpdateModel (this);
152+ }
153+ }
154+
155+ public @Player_UpdateCurrentModel (this) {
156+ if (equal (g_rgszCustomPlayerModel[this], NULL_STRING)) {
157+ if (! equal (g_rgszDefaultPlayerModel[this], NULL_STRING)) {
158+ static szModel[MAX_RESOURCE_PATH_LENGTH];
159+ format (szModel, charsmax (szModel), " models/player/%s /%s .mdl" , g_rgszDefaultPlayerModel[this], g_rgszDefaultPlayerModel[this]);
160+ copy (g_rgszCurrentPlayerModel[this], charsmax (g_rgszCurrentPlayerModel[]), szModel);
161+ }
79162 } else {
80- @Player_ResetModel ( this);
163+ copy (g_rgszCurrentPlayerModel[ this], charsmax (g_rgszCurrentPlayerModel[]), g_rgszCustomPlayerModel[this] );
81164 }
165+
166+ @Player_UpdateModel (this);
167+ }
168+
169+ public @Player_UpdateModel (this) {
170+ new iAnimationIndex = g_rgiPlayerAnimationIndex[this];
171+ new iModelIndex = engfunc (EngFunc_ModelIndex, g_rgszCurrentPlayerModel[this]);
172+ @Player_SetModelIndex (this, iAnimationIndex ? iAnimationIndex : iModelIndex);
173+ set_pev (g_pPlayerModel[this], pev_modelindex, iAnimationIndex ? iModelIndex : 0 );
82174}
83175
84176public @Player_ResetModel (this) {
85- if (equal (g_rgszPlayerModel [this], NULL_STRING)) {
177+ if (equal (g_rgszDefaultPlayerModel [this], NULL_STRING)) {
86178 return ;
87179 }
88-
89- static szPath[MAX_RESOURCE_PATH_LENGTH];
90- format (szPath, charsmax (szPath), " models/player/%s /%s .mdl" , g_rgszPlayerModel[this], g_rgszPlayerModel[this]);
91180
92- new iModelIndex = engfunc (EngFunc_ModelIndex, szPath);
93- set_user_info (this, " model" , g_rgszPlayerModel[this]);
181+ copy (g_rgszCustomPlayerModel[this], charsmax (g_rgszCustomPlayerModel[]), NULL_STRING);
182+ copy (g_rgszCurrentPlayerModel[this], charsmax (g_rgszCurrentPlayerModel[]), NULL_STRING);
183+ g_rgiPlayerAnimationIndex[this] = 0 ;
184+
185+ @Player_UpdateCurrentModel (this);
186+ }
187+
188+ public @Player_SetModelIndex (this, iModelIndex) {
189+ set_user_info (this, " model" , " " );
94190 set_pev (this, pev_modelindex, iModelIndex);
95191 set_member (this, m_modelIndexPlayer, iModelIndex);
96- copy (g_rgszCustomPlayerModel[this], charsmax (g_rgszCustomPlayerModel[]), NULL_STRING);
192+ }
193+
194+ public @Player_SetSequence (this, const szSequence[]) {
195+ new iAnimationIndex = GetAnimationIndexBySequence (szSequence);
196+ if (! iAnimationIndex) {
197+ return - 1 ;
198+ }
199+
200+ g_rgiPlayerAnimationIndex[this] = iAnimationIndex;
201+ @Player_UpdateModel (this);
202+
203+ new iSequence = GetSequenceIndex (szSequence);
204+ set_pev (this, pev_sequence, iSequence);
205+ return iSequence;
206+ }
207+
208+ GetAnimationIndexByAnimExt (const szAnimExt[]) {
209+ static szSequence[32 ];
210+ format (szSequence, charsmax (szSequence), " ref_aim_%s " , szAnimExt);
211+ return GetAnimationIndexBySequence (szSequence);
212+ }
213+
214+ GetAnimationIndexBySequence (const szSequence[]) {
215+ static iAnimationIndex;
216+ if (! TrieGetCell (g_itPlayerSequenceModelIndexes, szSequence, iAnimationIndex)) {
217+ return 0 ;
218+ }
219+
220+ return iAnimationIndex;
221+ }
222+
223+ GetSequenceIndex (const szSequence[]) {
224+ static iSequence;
225+ if (! TrieGetCell (g_itPlayerSequences, szSequence, iSequence)) {
226+ return - 1 ;
227+ }
228+
229+ return iSequence;
230+ }
231+
232+ // Credis: HamletEagle
233+ PrecachePlayerAnimation (const szAnim[]) {
234+ new szFilePath[MAX_RESOURCE_PATH_LENGTH];
235+ format (szFilePath, charsmax (szFilePath), " animations/%s " , szAnim);
236+
237+ new iModelIndex = precache_model (szFilePath);
238+
239+ new iFile = fopen (szFilePath, " rb" )
240+ if (! iFile) {
241+ return 0
242+ }
243+
244+ // Got to "numseq" position of the studiohdr_t structure
245+ // https://github.com/dreamstalker/rehlds/blob/65c6ce593b5eabf13e92b03352e4b429d0d797b0/rehlds/public/rehlds/studio.h#L68
246+ fseek (iFile, 164 , SEEK_SET);
247+
248+ new iSeqNum;
249+ fread (iFile, iSeqNum, BLOCK_INT);
250+
251+ if (! iSeqNum) {
252+ return 0 ;
253+ }
254+
255+ new iSeqIndex;
256+ fread (iFile, iSeqIndex, BLOCK_INT);
257+ fseek (iFile, iSeqIndex, SEEK_SET);
258+
259+ new szLabel[32 ];
260+ for (new i = 0 ; i < iSeqNum; i++ ) {
261+ if (i >= MAX_SEQUENCES) {
262+ log_amx (" Warning! Sequence limit reached for ^" % s^ " . Max sequences %d ." , szFilePath, MAX_SEQUENCES);
263+ break ;
264+ }
265+
266+ fread_blocks (iFile, szLabel, sizeof (szLabel), BLOCK_CHAR);
267+ TrieSetCell (g_itPlayerSequenceModelIndexes, szLabel, iModelIndex);
268+ TrieSetCell (g_itPlayerSequences, szLabel, i);
269+
270+ // jump to the end of the studiohdr_t structure
271+ // https://github.com/dreamstalker/rehlds/blob/65c6ce593b5eabf13e92b03352e4b429d0d797b0/rehlds/public/rehlds/studio.h#L95
272+ fseek (iFile, 176 - sizeof (szLabel), SEEK_CUR);
273+ }
274+
275+ fclose (iFile);
276+
277+ return iModelIndex;
97278}
0 commit comments