Skip to content

Commit 4d5ff38

Browse files
committed
add custome animations system
1 parent 8316048 commit 4d5ff38

File tree

2 files changed

+207
-26
lines changed

2 files changed

+207
-26
lines changed

api_player_model.sma

+205-24
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
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];
1114
new 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

1328
public 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

2143
public 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

2953
public 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

4568
public 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

5088
public 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

5598
public 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

59126
public 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

84176
public @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
}

include/api_player_model.inc

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// This is a beta version, some functions may be unstable. Use at your own risk.
2-
31
#if defined _api_player_model_included
42
#endinput
53
#endif
@@ -11,3 +9,5 @@ native PlayerModel_Get(pPlayer, szOut[]);
119
native PlayerModel_Set(pPlayer, const szModel[]);
1210
native PlayerModel_Update(pPlayer);
1311
native PlayerModel_Reset(pPlayer);
12+
native PlayerModel_PrecacheAnimation(const szAnimation[]);
13+
native PlayerModel_SetSequence(pPlayer, const szSequence[]);

0 commit comments

Comments
 (0)