Skip to content

Commit a6924e6

Browse files
committed
Merge tf2wearables functionality
I ended up maintaining that plugin anyways, so I might as well pull it in so there are fewer dependencies to manage.
1 parent 7902224 commit a6924e6

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

gamedata/tf2.utils.nosoop.txt

+20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
"windows" "86"
2121
"linux" "87"
2222
}
23+
"CBaseEntity::IsWearable()"
24+
{
25+
"windows" "87"
26+
"linux" "88"
27+
"mac" "88"
28+
}
2329
"CBaseEntity::TakeHealth()"
2430
{
2531
"windows" "64"
@@ -35,6 +41,12 @@
3541
"windows" "372"
3642
"linux" "378"
3743
}
44+
"CTFPlayer::EquipWearable()"
45+
{
46+
"windows" "430"
47+
"linux" "431"
48+
"mac" "431"
49+
}
3850

3951
// member offsets
4052
// any resemblance to the names present in official game code is purely coincidental
@@ -48,6 +60,14 @@
4860
}
4961
"Signatures"
5062
{
63+
"CTFPlayer::GetEntityForLoadoutSlot()"
64+
{
65+
// called a few blocks after function with unique x-ref string "enable_misc2_noisemaker"
66+
"library" "server"
67+
"windows" "\x55\x8b\xec\x51\x53\x8b\x5d\x2a\x57\x8b\xf9\x89\x7d\x2a\x83\xfb\x07\x74\x2a\x83\xfb\x08\x74\x2a\x83\xfb\x09\x74\x2a\x83\xfb\x0a\x74\x2a"
68+
"linux" "@_ZN9CTFPlayer23GetEntityForLoadoutSlotEib"
69+
"mac" "@_ZN9CTFPlayer23GetEntityForLoadoutSlotEib"
70+
}
5171
"CTFPlayer::GetMaxAmmo()"
5272
{
5373
"library" "server"

scripting/include/tf2utils.inc

+34
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ native void TF2Util_GetPlayerShootPosition(int client, float result[3]);
6060
*/
6161
native bool TF2Util_IsEntityWeapon(int entity);
6262

63+
/**
64+
* Returns whether or not the given entity is a wearable.
65+
*
66+
* @error Entity is not valid.
67+
*/
68+
native bool TF2Util_IsEntityWearable(int entity);
69+
6370
/**
6471
* Returns the slot for the given weapon entity.
6572
*
@@ -93,6 +100,26 @@ native int TF2Util_GetPlayerWearable(int client, int index);
93100
*/
94101
native int TF2Util_GetPlayerWearableCount(int client);
95102

103+
/**
104+
* If `includeWearableWeapons` is true, weapon slots (primary, secondary, melee, utility,
105+
* building, pda, pda2) are also checked for wearable items. Otherwise, non-wearables are
106+
* ignored.
107+
*
108+
* Note that this validates that the player is using a class that can equip the given item; any
109+
* items not applicable to the class are ignored.
110+
*
111+
* @return Entity on the given client occupying the given loadout slot.
112+
*/
113+
native int TF2Util_GetPlayerLoadoutEntity(int client, int loadoutSlot,
114+
bool includeWearableWeapons = true);
115+
116+
/**
117+
* Equips the given wearable entity onto the given client.
118+
*
119+
* @error Client is invalid or entity is not a wearable.
120+
*/
121+
native void TF2Util_EquipPlayerWearable(int client, int wearable);
122+
96123
/**
97124
* Forces an update to the given player's speed.
98125
*
@@ -118,6 +145,13 @@ native void TF2Util_UpdatePlayerSpeed(int client, bool immediate = false);
118145
native bool TF2Util_IsPointInRespawnRoom(const float[3] position,
119146
int entity = INVALID_ENT_REFERENCE, bool bRestrictToSameTeam = false);
120147

148+
// compile-time compatibility shim for tf2wearables natives
149+
#if defined USE_TF2WEARABLE_FUNCTION_SHIMS
150+
#define TF2_GetPlayerLoadoutSlot TF2Util_GetPlayerLoadoutEntity
151+
#define TF2_EquipPlayerWearable TF2Util_EquipPlayerWearable
152+
#define TF2_IsWearable TF2Util_IsEntityWearable
153+
#endif
154+
121155
public SharedPlugin __pl_tf2utils = {
122156
name = "nosoop_tf2utils",
123157
file = "tf2utils.smx",

scripting/tf2utils.sp

+67-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
#include <stocksoup/memory>
1313

14-
#define PLUGIN_VERSION "0.10.0"
14+
#define PLUGIN_VERSION "0.11.0"
1515
public Plugin myinfo = {
1616
name = "TF2 Utils",
1717
author = "nosoop",
@@ -26,6 +26,7 @@ bool g_bDeferredSpeedUpdate[MAXPLAYERS + 1];
2626
Handle g_SDKCallPlayerGetMaxAmmo;
2727
Handle g_SDKCallPlayerTakeHealth;
2828
Handle g_SDKCallPlayerGetShootPosition;
29+
Handle g_SDKCallPlayerGetEntityForLoadoutSlot;
2930

3031
Handle g_SDKCallPlayerSharedGetMaxHealth;
3132

@@ -34,6 +35,9 @@ Handle g_SDKCallWeaponGetSlot;
3435
Handle g_SDKCallWeaponGetID;
3536
Handle g_SDKCallWeaponGetMaxClip;
3637

38+
Handle g_SDKCallIsEntityWearable;
39+
Handle g_SDKCallPlayerEquipWearable;
40+
3741
Handle g_SDKCallPointInRespawnRoom;
3842

3943
Address offs_CTFPlayer_hMyWearables;
@@ -46,14 +50,18 @@ public APLRes AskPluginLoad2(Handle self, bool late, char[] error, int maxlen) {
4650
CreateNative("TF2Util_GetPlayerMaxHealth", Native_GetMaxHealth);
4751
CreateNative("TF2Util_GetPlayerMaxAmmo", Native_GetMaxAmmo);
4852

53+
CreateNative("TF2Util_IsEntityWearable", Native_IsEntityWearable);
4954
CreateNative("TF2Util_GetPlayerWearable", Native_GetPlayerWearable);
5055
CreateNative("TF2Util_GetPlayerWearableCount", Native_GetPlayerWearableCount);
56+
CreateNative("TF2Util_EquipPlayerWearable", Native_EquipPlayerWearable);
5157

5258
CreateNative("TF2Util_IsEntityWeapon", Native_IsEntityWeapon);
5359
CreateNative("TF2Util_GetWeaponSlot", Native_GetWeaponSlot);
5460
CreateNative("TF2Util_GetWeaponID", Native_GetWeaponID);
5561
CreateNative("TF2Util_GetWeaponMaxClip", Native_GetWeaponMaxClip);
5662

63+
CreateNative("TF2Util_GetPlayerLoadoutEntity", Native_GetPlayerLoadoutEntity);
64+
5765
CreateNative("TF2Util_GetPlayerShootPosition", Native_GetPlayerShootPosition);
5866

5967
CreateNative("TF2Util_IsPointInRespawnRoom", Native_IsPointInRespawnRoom);
@@ -104,6 +112,11 @@ public void OnPluginStart() {
104112
PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain);
105113
g_SDKCallIsEntityWeapon = EndPrepSDKCall();
106114

115+
StartPrepSDKCall(SDKCall_Entity);
116+
PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "CBaseEntity::IsWearable()");
117+
PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain);
118+
g_SDKCallIsEntityWearable = EndPrepSDKCall();
119+
107120
StartPrepSDKCall(SDKCall_Entity);
108121
PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "CBaseCombatWeapon::GetSlot()");
109122
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
@@ -119,6 +132,19 @@ public void OnPluginStart() {
119132
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
120133
g_SDKCallWeaponGetMaxClip = EndPrepSDKCall();
121134

135+
StartPrepSDKCall(SDKCall_Player);
136+
PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature,
137+
"CTFPlayer::GetEntityForLoadoutSlot()");
138+
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
139+
PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
140+
PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity, SDKPass_Pointer);
141+
g_SDKCallPlayerGetEntityForLoadoutSlot = EndPrepSDKCall();
142+
143+
StartPrepSDKCall(SDKCall_Player);
144+
PrepSDKCall_SetFromConf(hGameConf, SDKConf_Virtual, "CTFPlayer::EquipWearable()");
145+
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
146+
g_SDKCallPlayerEquipWearable = EndPrepSDKCall();
147+
122148
StartPrepSDKCall(SDKCall_Static);
123149
PrepSDKCall_SetFromConf(hGameConf, SDKConf_Signature, "PointInRespawnRoom()");
124150
PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain);
@@ -221,6 +247,20 @@ public int Native_GetMaxHealth(Handle plugin, int nParams) {
221247
bIgnoreAttributes, bIgnoreOverheal);
222248
}
223249

250+
public int Native_EquipPlayerWearable(Handle plugin, int numParams) {
251+
int client = GetNativeCell(1);
252+
if (client < 1 || client > MaxClients || !IsClientInGame(client)) {
253+
ThrowNativeError(SP_ERROR_NATIVE, "Client index %d is invalid", client);
254+
}
255+
256+
int wearable = GetNativeCell(2);
257+
if (!IsEntityWearable(wearable)) {
258+
ThrowNativeError(SP_ERROR_NATIVE, "Entity index %d is not a wearable",
259+
EntRefToEntIndex(wearable));
260+
}
261+
SDKCall(g_SDKCallPlayerEquipWearable, client, wearable);
262+
}
263+
224264
// int(int client, int index);
225265
public int Native_GetPlayerWearable(Handle plugin, int nParams) {
226266
int client = GetNativeCell(1);
@@ -266,6 +306,12 @@ public int Native_IsEntityWeapon(Handle plugin, int nParams) {
266306
return IsEntityWeapon(entity);
267307
}
268308

309+
// bool(int entity);
310+
public int Native_IsEntityWearable(Handle plugin, int nParams) {
311+
int entity = GetNativeCell(1);
312+
return IsEntityWearable(entity);
313+
}
314+
269315
// int(int entity);
270316
public int Native_GetWeaponSlot(Handle plugin, int nParams) {
271317
int entity = GetNativeCell(1);
@@ -316,6 +362,18 @@ public int Native_IsPointInRespawnRoom(Handle plugin, int nParams) {
316362
return SDKCall(g_SDKCallPointInRespawnRoom, entity, origin, bRestrictToSameTeam);
317363
}
318364

365+
// int(int client, int loadoutSlot, bool includeWearableWeapons);
366+
int Native_GetPlayerLoadoutEntity(Handle plugin, int numParams) {
367+
int client = GetNativeCell(1);
368+
if (client < 1 || client > MaxClients || !IsClientInGame(client)) {
369+
ThrowNativeError(SP_ERROR_NATIVE, "Client index %d is invalid", client);
370+
}
371+
int loadoutSlot = GetNativeCell(2);
372+
bool check_wearable = numParams <3 ? true : GetNativeCell(3);
373+
374+
return SDKCall(g_SDKCallPlayerGetEntityForLoadoutSlot, client, loadoutSlot, check_wearable);
375+
}
376+
319377
bool IsEntityWeapon(int entity) {
320378
if (!IsValidEntity(entity)) {
321379
ThrowNativeError(SP_ERROR_NATIVE, "Entity %d (%d) is invalid", entity,
@@ -324,6 +382,14 @@ bool IsEntityWeapon(int entity) {
324382
return SDKCall(g_SDKCallIsEntityWeapon, entity);
325383
}
326384

385+
bool IsEntityWearable(int entity) {
386+
if (!IsValidEntity(entity)) {
387+
ThrowNativeError(SP_ERROR_NATIVE, "Entity %d (%d) is invalid", entity,
388+
EntRefToEntIndex(entity));
389+
}
390+
return SDKCall(g_SDKCallIsEntityWearable, entity);
391+
}
392+
327393
static Address GameConfGetAddressOffset(Handle gamedata, const char[] key) {
328394
Address offs = view_as<Address>(GameConfGetOffset(gamedata, key));
329395
if (offs == view_as<Address>(-1)) {

0 commit comments

Comments
 (0)