diff --git a/extensions/tf2/natives.cpp b/extensions/tf2/natives.cpp index 8e103dcd..a83e4787 100644 --- a/extensions/tf2/natives.cpp +++ b/extensions/tf2/natives.cpp @@ -159,6 +159,120 @@ cell_t TF2_RemoveDisguise(IPluginContext *pContext, const cell_t *params) return 1; } +cell_t TF2_AddCondition(IPluginContext *pContext, const cell_t *params) +{ + static ICallWrapper *pWrapper = NULL; + + // CTFPlayerShared::AddCond(int, float) + if (!pWrapper) + { + REGISTER_NATIVE_ADDR("AddCondition", + PassInfo pass[2]; \ + pass[0].flags = PASSFLAG_BYVAL; \ + pass[0].size = sizeof(int); \ + pass[0].type = PassType_Basic; \ + pass[1].flags = PASSFLAG_BYVAL; \ + pass[1].size = sizeof(float); \ + pass[1].type = PassType_Basic; \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2)) + } + + CBaseEntity *pEntity; + if (!(pEntity = UTIL_GetCBaseEntity(params[1], true))) + { + return pContext->ThrowNativeError("Client index %d is not valid", params[1]); + } + + void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + + unsigned char vstk[sizeof(void *) + sizeof(int) + sizeof(float)]; + unsigned char *vptr = vstk; + + *(void **)vptr = obj; + vptr += sizeof(void *); + *(int *)vptr = params[2]; + vptr += sizeof(int); + *(float *)vptr = *(float *)¶ms[3]; + + pWrapper->Execute(vstk, NULL); + + return 1; +} + +cell_t TF2_RemoveCondition(IPluginContext *pContext, const cell_t *params) +{ + static ICallWrapper *pWrapper = NULL; + + // CTFPlayerShared::RemoveCond(int) + if (!pWrapper) + { + REGISTER_NATIVE_ADDR("RemoveCondition", + PassInfo pass[1]; \ + pass[0].flags = PASSFLAG_BYVAL; \ + pass[0].size = sizeof(int); \ + pass[0].type = PassType_Basic; \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1)) + } + + CBaseEntity *pEntity; + if (!(pEntity = UTIL_GetCBaseEntity(params[1], true))) + { + return pContext->ThrowNativeError("Client index %d is not valid", params[1]); + } + + void *obj = (void *)((uint8_t *)pEntity + playerSharedOffset->actual_offset); + + unsigned char vstk[sizeof(void *) + sizeof(int)]; + unsigned char *vptr = vstk; + + *(void **)vptr = obj; + vptr += sizeof(void *); + *(int *)vptr = params[2]; + + pWrapper->Execute(vstk, NULL); + + return 1; +} + +cell_t TF2_SetPowerplayEnabled(IPluginContext *pContext, const cell_t *params) +{ + static ICallWrapper *pWrapper = NULL; + + // CTFPlayer::SetPowerPlayEnabled(bool) + if (!pWrapper) + { + REGISTER_NATIVE_ADDR("SetPowerplayEnabled", + PassInfo pass[1]; \ + pass[0].flags = PASSFLAG_BYVAL; \ + pass[0].size = sizeof(bool); \ + pass[0].type = PassType_Basic; \ + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 1)) + } + + CBaseEntity *pEntity; + if (!(pEntity = UTIL_GetCBaseEntity(params[1], true))) + { + return pContext->ThrowNativeError("Client index %d is not valid", params[1]); + } + + bool bEnablePP = false; + if (params[2] != 0) + { + bEnablePP = true; + } + + unsigned char vstk[sizeof(void *) + sizeof(bool)]; + unsigned char *vptr = vstk; + + *(void **)vptr = (void *)pEntity; + vptr += sizeof(void *); + *(bool *)vptr = bEnablePP; + + pWrapper->Execute(vstk, NULL); + + return 1; +} + cell_t TF2_Respawn(IPluginContext *pContext, const cell_t *params) { static ICallWrapper *pWrapper = NULL; @@ -201,6 +315,28 @@ cell_t TF2_Respawn(IPluginContext *pContext, const cell_t *params) return 1; } +cell_t TF2_Regenerate(IPluginContext *pContext, const cell_t *params) +{ + static ICallWrapper *pWrapper = NULL; + + //CTFPlayer::Regenerate() + + if (!pWrapper) + { + REGISTER_NATIVE_ADDR("Regenerate", + pWrapper = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, NULL, 0)); + } + + CBaseEntity *pEntity; + if (!(pEntity = UTIL_GetCBaseEntity(params[1], true))) + { + return pContext->ThrowNativeError("Client index %d is not valid", params[1]); + } + pWrapper->Execute(&pEntity, NULL); + + return 1; +} + cell_t TF2_GetResourceEntity(IPluginContext *pContext, const cell_t *params) { return g_resourceEntity; @@ -223,5 +359,9 @@ sp_nativeinfo_t g_TFNatives[] = {"TF2_RemovePlayerDisguise", TF2_RemoveDisguise}, {"TF2_GetResourceEntity", TF2_GetResourceEntity}, {"TF2_GetClass", TF2_GetClass}, + {"TF2_RegeneratePlayer", TF2_Regenerate}, + {"TF2_AddCondition", TF2_AddCondition}, + {"TF2_RemoveCondition", TF2_RemoveCondition}, + {"TF2_SetPlayerPowerPlay", TF2_SetPowerplayEnabled}, {NULL, NULL} }; diff --git a/gamedata/sm-tf2.games.txt b/gamedata/sm-tf2.games.txt index 3ef8450d..77f40438 100644 --- a/gamedata/sm-tf2.games.txt +++ b/gamedata/sm-tf2.games.txt @@ -51,6 +51,30 @@ "linux" "@_ZN14CTFCompoundBow26CalcIsAttackCriticalHelperEv" "windows" "\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x14\x6A\x0B" } + "Regenerate" + { + "library" "server" + "windows" "\x83\xEC\x08\xD9\xE8\x53\x55\xD9\x2A\x2A\x2A\x56\x8B\xF1\x8B\x06\x8B\x90" + "linux" "@_ZN9CTFPlayer10RegenerateEv" + } + "AddCondition" + { + "library" "server" + "windows" "\x56\x57\x8B\x7C\x2A\x2A\x8B\xF1\x8B\xCF\xBA\x01\x00\x00\x00\xD3\xE2\x8D\x46\x2A\x8D\x4C\x2A\x2A\x51\x0B\x10" + "linux" "@_ZN15CTFPlayerShared7AddCondEif" + } + "RemoveCondition" + { + "library" "server" + "windows" "\x56\x57\x8B\x7C\x2A\x2A\x8B\xF1\x8B\xCF\xBA\x01\x00\x00\x00\xD3\xE2\x8D\x46\x2A\x8D\x4C\x2A\x2A\x51\xF7\xD2" + "linux" "@_ZN15CTFPlayerShared10RemoveCondEi" + } + "SetPowerplayEnabled" + { + "library" "server" + "windows" "\x80\x7C\x2A\x2A\x2A\x56\x8B\xF1\x74\x2A\xA1\x2A\x2A\x2A\x2A\xD9\x2A\x2A\x57" + "linux" "@_ZN9CTFPlayer19SetPowerplayEnabledEb" + } } "Offsets" { diff --git a/plugins/include/tf2.inc b/plugins/include/tf2.inc index cb1f209b..bd8123fb 100644 --- a/plugins/include/tf2.inc +++ b/plugins/include/tf2.inc @@ -57,6 +57,33 @@ enum TFTeam TFTeam_Blue = 3 }; +enum TFCond +{ + TFCond_Slowed = 0, + TFCond_Zoomed, + TFCond_Disguising, + TFCond_Disguised, + TFCond_Cloaked, + TFCond_Ubercharged, + TFCond_TeleportedGlow, + TFCond_Taunting, + TFCond_UberchargeFading, + TFCond_Unknown1, + TFCond_Teleporting, + TFCond_Kritzkrieged, + TFCond_Unknown2, + TFCond_DeadRingered, + TFCond_Bonked, + TFCond_Dazed, + TFCond_Buffed, + TFCond_Charging, + TFCond_DemoBuff, + TFCond_Healing, + TFCond_OnFire, + TFCond_Overhealed, + TFCond_Jarated +} + /** * Sets a client on fire for 10 seconds. * @@ -75,6 +102,46 @@ native TF2_IgnitePlayer(client, target); */ native TF2_RespawnPlayer(client); +/** + * Regenerates a client's health and ammunition + * + * @param client Player's index. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_RegeneratePlayer(client); + +/** + * Adds a condition to a player + * + * @param client Player's index. + * @param condition Integer identifier of condition to apply. + * @param duration Duration of condition (does not apply to all conditions). + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_AddCondition(client, TFCond:condition, Float:duration); + +/** + * Removes a condition from a player + * + * @param client Player's index. + * @param condition Integer identifier of condition to remove. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_RemoveCondition(client, TFCond:condition); + +/** + * Enables/disables PowerPlay mode on a player. + * + * @param client Player's index. + * @param enabled Whether to enable or disable PowerPlay on player. + * @noreturn + * @error Invalid client index, client not in game, or no mod support. + */ +native TF2_SetPlayerPowerPlay(client, bool:enabled); + /** * Disguises a client to the given model and team. Only has an effect on spies. * diff --git a/plugins/testsuite/tf2-test.sp b/plugins/testsuite/tf2-test.sp index 6623d33a..4efeb85c 100644 --- a/plugins/testsuite/tf2-test.sp +++ b/plugins/testsuite/tf2-test.sp @@ -15,25 +15,21 @@ public Plugin:myinfo = public OnPluginStart() { RegConsoleCmd("sm_burnme", Command_Burn); - RegConsoleCmd("sm_invuln", Command_Invuln); RegConsoleCmd("sm_respawn", Command_Respawn); RegConsoleCmd("sm_disguise", Command_Disguise); RegConsoleCmd("sm_remdisguise", Command_RemDisguise); RegConsoleCmd("sm_class", Command_Class); RegConsoleCmd("sm_remove", Command_Remove); RegConsoleCmd("sm_changeclass", Command_ChangeClass); + RegConsoleCmd("sm_regenerate", Command_Regenerate); + RegConsoleCmd("sm_uberme", Command_UberMe); + RegConsoleCmd("sm_unuberme", Command_UnUberMe); + RegConsoleCmd("sm_setpowerplay", Command_SetPowerPlay); } public Action:Command_Class(client, args) { TF2_RemoveAllWeapons(client); - - decl String:text[10]; - GetCmdArg(1, text, sizeof(text)); - - new one = StringToInt(text); - - TF2_EquipPlayerClassWeapons(client, TFClassType:one); PrintToChat(client, "Test: sniper's classnum is %i (should be %i)", TF2_GetClass("sniper"), TFClass_Sniper); @@ -93,28 +89,6 @@ public Action:Command_Burn(client, args) return Plugin_Handled; } -public Action:Command_Invuln(client, args) -{ - if (client == 0) - { - return Plugin_Handled; - } - - if (args < 1) - { - return Plugin_Handled; - } - - decl String:text[10]; - GetCmdArg(1, text, sizeof(text)); - - new bool:one = !!StringToInt(text); - - TF2_SetPlayerInvuln(client, one) - - return Plugin_Handled; -} - public Action:Command_Disguise(client, args) { if (client == 0) @@ -165,3 +139,71 @@ public Action:Command_Respawn(client, args) return Plugin_Handled; } + +public Action:Command_Regenerate(client, args) +{ + if (client == 0) + { + return Plugin_Handled; + } + + TF2_RegeneratePlayer(client); + + return Plugin_Handled; +} + +public Action:Command_UberMe(client, args) +{ + if (client == 0) + { + return Plugin_Handled; + } + + if (args < 1) + { + return Plugin_Handled; + } + + decl String:text[10]; + GetCmdArg(1, text, sizeof(text)); + + new Float:one = StringToFloat(text); + + TF2_AddCondition(client, TFCond_Ubercharged, one); + + return Plugin_Handled; +} + +public Action:Command_UnUberMe(client, args) +{ + if (client == 0) + { + return Plugin_Handled; + } + + TF2_RemoveCondition(client, TFCond_Ubercharged); + + return Plugin_Handled; +} + +public Action:Command_SetPowerPlay(client, args) +{ + if (client == 0) + { + return Plugin_Handled; + } + + if (args < 1) + { + return Plugin_Handled; + } + + decl String:text[10]; + GetCmdArg(1, text, sizeof(text)); + + new bool:one = bool:StringToInt(text); + + TF2_SetPlayerPowerPlay(client, one); + + return Plugin_Handled; +} \ No newline at end of file