-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathcs_player_models_api.sma
244 lines (193 loc) · 6.3 KB
/
cs_player_models_api.sma
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*================================================================================
----------------------------------
-*- [CS] Player Models API 1.2 -*-
----------------------------------
- Allows easily setting and restoring custom player models in CS and CZ
(models last until player disconnects or are manually reset)
- Built-in SVC_BAD prevention
- Support for custom hitboxes (model index offset setting)
- You still need to precache player models in your plugin!
Original thread:
http://forums.alliedmods.net/showthread.php?t=161255
================================================================================*/
// Delay between model changes (increase if getting SVC_BAD kicks)
#define MODELCHANGE_DELAY 0.2
// Delay after roundstart (increase if getting kicks at round start)
#define ROUNDSTART_DELAY 2.0
// Enable custom hitboxes (experimental, might lag your server badly with some models)
//#define SET_MODELINDEX_OFFSET
/*=============================================================================*/
#include <amxmodx>
#include <cstrike>
#include <fakemeta>
#define MODELNAME_MAXLENGTH 32
#define TASK_MODELCHANGE 100
#define ID_MODELCHANGE (taskid - TASK_MODELCHANGE)
new const DEFAULT_MODELINDEX_T[] = "models/player/terror/terror.mdl"
new const DEFAULT_MODELINDEX_CT[] = "models/player/urban/urban.mdl"
// CS Player PData Offsets (win32)
#define PDATA_SAFE 2
#define OFFSET_CSTEAMS 114
#define OFFSET_MODELINDEX 491 // Orangutanz
#define flag_get(%1,%2) (%1 & (1 << (%2 & 31)))
#define flag_set(%1,%2) (%1 |= (1 << (%2 & 31)));
#define flag_unset(%1,%2) (%1 &= ~(1 << (%2 & 31)));
#define ADMIN_FLAG_X (1<<23)
new g_MaxPlayers
new g_HasCustomModel
new Float:g_ModelChangeTargetTime
new g_CustomPlayerModel[MAX_PLAYERS+1][MODELNAME_MAXLENGTH]
#if defined SET_MODELINDEX_OFFSET
new g_CustomModelIndex[MAX_PLAYERS+1]
#endif
public plugin_init()
{
register_plugin("[CS] Player Models API", "1.2", "WiLS")
register_event("HLTV", "event_round_start", "a", "1=0", "2=0")
register_forward(FM_SetClientKeyValue, "fw_SetClientKeyValue")
g_MaxPlayers = get_maxplayers()
}
public plugin_natives()
{
register_library("cs_player_models_api")
register_native("cs_set_player_model", "native_set_player_model")
register_native("cs_reset_player_model", "native_reset_player_model")
}
public native_set_player_model(plugin_id, num_params)
{
new id = get_param(1)
if (!is_user_connected(id))
{
log_error(AMX_ERR_NATIVE, "[CS] Player is not in game (%d)", id)
return false;
}
new newmodel[MODELNAME_MAXLENGTH]
get_string(2, newmodel, charsmax(newmodel))
remove_task(id+TASK_MODELCHANGE)
flag_set(g_HasCustomModel, id)
copy(g_CustomPlayerModel[id], charsmax(g_CustomPlayerModel[]), newmodel)
#if defined SET_MODELINDEX_OFFSET
new modelpath[32+(2*MODELNAME_MAXLENGTH)]
formatex(modelpath, charsmax(modelpath), "models/player/%s/%s.mdl", newmodel, newmodel)
g_CustomModelIndex[id] = engfunc(EngFunc_ModelIndex, modelpath)
#endif
new currentmodel[MODELNAME_MAXLENGTH]
fm_cs_get_user_model(id, currentmodel, charsmax(currentmodel))
if (!equal(currentmodel, newmodel))
fm_cs_user_model_update(id+TASK_MODELCHANGE)
return true;
}
public native_reset_player_model(plugin_id, num_params)
{
new id = get_param(1)
if (!is_user_connected(id))
{
log_error(AMX_ERR_NATIVE, "[CS] Player is not in game (%d)", id)
return false
}
// Player doesn't have a custom model, no need to reset
if (!flag_get(g_HasCustomModel, id))
return true
remove_task(id+TASK_MODELCHANGE)
flag_unset(g_HasCustomModel, id)
fm_cs_reset_user_model(id)
return true
}
public client_disconnected(id)
{
remove_task(id+TASK_MODELCHANGE)
flag_unset(g_HasCustomModel, id)
}
public event_round_start()
{
// An additional delay is offset at round start
// since SVC_BAD is more likely to be triggered there
g_ModelChangeTargetTime = get_gametime() + ROUNDSTART_DELAY
// If a player has a model change task in progress,
// reschedule the task, since it could potentially
// be executed during roundstart
new player
for (player = 1; player <= g_MaxPlayers; player++)
{
if (task_exists(player+TASK_MODELCHANGE))
{
remove_task(player+TASK_MODELCHANGE)
fm_cs_user_model_update(player+TASK_MODELCHANGE)
}
}
}
public fw_SetClientKeyValue(id, const infobuffer[], const key[], const value[])
{
if (flag_get(g_HasCustomModel, id) && equal(key, "model"))
{
static currentmodel[MODELNAME_MAXLENGTH]
fm_cs_get_user_model(id, currentmodel, charsmax(currentmodel))
if (!equal(currentmodel, g_CustomPlayerModel[id]) && !task_exists(id+TASK_MODELCHANGE))
fm_cs_set_user_model(id+TASK_MODELCHANGE)
#if defined SET_MODELINDEX_OFFSET
fm_cs_set_user_model_index(id)
#endif
return FMRES_SUPERCEDE;
}
return FMRES_IGNORED;
}
public fm_cs_set_user_model(taskid)
{
set_user_info(ID_MODELCHANGE, "model", g_CustomPlayerModel[ID_MODELCHANGE])
}
stock fm_cs_set_user_model_index(id)
{
if (pev_valid(id) != PDATA_SAFE)
return;
set_pdata_int(id, OFFSET_MODELINDEX, g_CustomModelIndex[id])
}
stock fm_cs_reset_user_model_index(id)
{
if (pev_valid(id) != PDATA_SAFE)
return;
switch (fm_cs_get_user_team(id))
{
case CS_TEAM_T:
{
set_pdata_int(id, OFFSET_MODELINDEX, engfunc(EngFunc_ModelIndex, DEFAULT_MODELINDEX_T))
}
case CS_TEAM_CT:
{
set_pdata_int(id, OFFSET_MODELINDEX, engfunc(EngFunc_ModelIndex, DEFAULT_MODELINDEX_CT))
}
}
}
stock fm_cs_get_user_model(id, model[], len)
{
get_user_info(id, "model", model, len)
}
stock fm_cs_reset_user_model(id)
{
// Set some generic model and let CS automatically reset player model to default
copy(g_CustomPlayerModel[id], charsmax(g_CustomPlayerModel[]), "gordon")
fm_cs_user_model_update(id+TASK_MODELCHANGE)
#if defined SET_MODELINDEX_OFFSET
fm_cs_reset_user_model_index(id)
#endif
}
stock fm_cs_user_model_update(taskid)
{
new Float:current_time
current_time = get_gametime()
if (current_time - g_ModelChangeTargetTime >= MODELCHANGE_DELAY)
{
fm_cs_set_user_model(taskid)
g_ModelChangeTargetTime = current_time
}
else
{
set_task((g_ModelChangeTargetTime + MODELCHANGE_DELAY) - current_time, "fm_cs_set_user_model", taskid)
g_ModelChangeTargetTime = g_ModelChangeTargetTime + MODELCHANGE_DELAY
}
}
stock CsTeams:fm_cs_get_user_team(id)
{
if (pev_valid(id) != PDATA_SAFE)
return CS_TEAM_UNASSIGNED;
return CsTeams:get_pdata_int(id, OFFSET_CSTEAMS);
}