From da518fdc0f32839730ccdee8098b59c6f842d93f Mon Sep 17 00:00:00 2001 From: navewindre Date: Mon, 13 Nov 2023 14:28:08 +0100 Subject: ya --- sourcemod/scripting/bot2player_public.sp | 462 +++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 sourcemod/scripting/bot2player_public.sp (limited to 'sourcemod/scripting/bot2player_public.sp') diff --git a/sourcemod/scripting/bot2player_public.sp b/sourcemod/scripting/bot2player_public.sp new file mode 100644 index 0000000..5faab4f --- /dev/null +++ b/sourcemod/scripting/bot2player_public.sp @@ -0,0 +1,462 @@ +#include +#include +#include + +new const String:PLUGIN_NAME[]= "CS:S Bot2Player (public)" +new const String: PLUGIN_AUTHOR[]= "Bittersweet" +new const String:PLUGIN_DESCRIPTION[]= "Allows players to control bots after they've died (adapted from botcontrol for TF2 by Grognak)" +new const String: PLUGIN_VERSION[]= "public.2013.06.23.23.46" +new Handle:cvar_b2p_enabled = INVALID_HANDLE +new Handle:cvar_Round_Restart_Delay = INVALID_HANDLE +new Handle:cvar_BotTakeOverStartingCost = INVALID_HANDLE +new Handle:cvar_BotTakeOverCostIncrement = INVALID_HANDLE + +//These are default values, actually set from bot2player_public.cfg file +new BotTakeOverStartingCost = 1000 +new BotTakeOverCostIncrement = 250 + +new bool:bHideDeath[MAXPLAYERS + 1] = {false, ...}; +new ClientSpecClient[MAXPLAYERS + 1] = {0, ...} +new ClientTookover[MAXPLAYERS + 1] = {0, ...} +new WrongTeamWarning[MAXPLAYERS + 1] = {0, ...} +new TeleportWarning[MAXPLAYERS + 1] = {0, ...} +new BotTakeverCost[MAXPLAYERS + 1] = {0, ...} +new Nades[MAXPLAYERS + 1][3] + +new Float:Round_Restart_Delay = 0.0 +new Float:Weapon_Strip_Delay = 0.0 +new bool:b2pEnabled +new iTargetActiveWeapon +new g_offObserverTarget +new iTargetWeapon[5] +new iTargetClip[5] +new iTargetAmmo[5] +new g_iAccount = -1 +new gameround = 1 + +new String:iTargetActiveWeaponName[32] + +public Plugin:myinfo = +{ + name = PLUGIN_NAME, + author = PLUGIN_AUTHOR, + description = PLUGIN_DESCRIPTION, + version = PLUGIN_VERSION, +} +public OnPluginStart() +{ + PrintToServer("[%s %s] - Loaded", PLUGIN_NAME, PLUGIN_VERSION) + HookEvent("player_death", Event_PlayerDeath, EventHookMode_Pre) + HookEvent("round_start", Event_RoundStart) + HookEvent("round_end", Event_RoundEnd, EventHookMode_Pre) + // round_freeze_end only used for debugging + //HookEvent("round_freeze_end", Event_RoundFreezeEnd) + g_iAccount = FindSendPropOffs("CCSPlayer", "m_iAccount"); + g_offObserverTarget = FindSendPropOffs("CBasePlayer", "m_hObserverTarget") + if(g_offObserverTarget == -1) + { + SetFailState("Expected to find the offset to m_hObserverTarget, couldn't.") + } + AddCommandListener(NewTarget, "spec_next") + AddCommandListener(NewTarget, "spec_prev") + CreateConVar("bot2player_version", PLUGIN_VERSION, "Bot2player (public) version", FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_DONTRECORD) + cvar_b2p_enabled = CreateConVar("bot2player_enabled", "1", "Enable the plugin?", FCVAR_PLUGIN, true, 0.0, true, 1.0) + cvar_BotTakeOverStartingCost = CreateConVar("bot2player_price", "1000", "Starting cost to take over a BOT (resets each map)", FCVAR_PLUGIN) + cvar_BotTakeOverCostIncrement = CreateConVar("bot2player_increase", "250", "Amount to raise price each time a player takes over a BOT", FCVAR_PLUGIN) + if (cvar_b2p_enabled == INVALID_HANDLE) + { + new String:FailReason[256] + Format(FailReason, sizeof(FailReason), "[%s] - cvar_b2p_enabled returned INVALID_HANDLE", PLUGIN_NAME) + EpicFail(FailReason) + } + cvar_Round_Restart_Delay = FindConVar("mp_round_restart_delay") + if (cvar_Round_Restart_Delay == INVALID_HANDLE) + { + new String:FailReason[256] + Format(FailReason, sizeof(FailReason), "[%s] - cvar_b2p_enabled returned INVALID_HANDLE", PLUGIN_NAME) + EpicFail(FailReason) + } + AutoExecConfig(true, "bot2player_public") +} +public OnConfigsExecuted() +{ + //based on 5.0 second mp_round_restart_delay: 4.6 just a hair early, 5.0 too late - 4.7 seems good - use mp_round_restart_delay - 0.3 + Round_Restart_Delay = GetConVarFloat(cvar_Round_Restart_Delay) + Weapon_Strip_Delay = Round_Restart_Delay - 0.3 + b2pEnabled = GetConVarBool(cvar_b2p_enabled) + BotTakeOverStartingCost = GetConVarInt(cvar_BotTakeOverStartingCost) + BotTakeOverCostIncrement = GetConVarInt(cvar_BotTakeOverCostIncrement) + HookConVarChange(cvar_b2p_enabled, cvar_b2p_enabledChange) + HookConVarChange(cvar_Round_Restart_Delay, cvar_b2p_enabledChange) + HookConVarChange(cvar_BotTakeOverStartingCost, CvarStartCostChange) + HookConVarChange(cvar_BotTakeOverCostIncrement, CvarCostIncreaseChange) +} +public OnMapStart() +{ + for (new i = 1; i <= MAXPLAYERS; i++) + { + BotTakeverCost[i] = BotTakeOverStartingCost + TeleportWarning[i] = 0 + } + gameround = 1 +} +public Action:Event_RoundStart(Handle:Event, const String:name[], bool:dontBroadcast) +{ + for (new i = 1; i <= MaxClients; i++) + { + ClientSpecClient[i] = 0 + WrongTeamWarning[i] = 0 + } +} +public Action:Event_RoundEnd(Handle:Event, const String:name[], bool:dontBroadcast) +{ + for (new i = 1; i <= MaxClients; i++) + { + ClientSpecClient[i] = 0 + if (IsClientConnected(i) && IsClientInGame(i) && !IsClientObserver(i) && ClientTookover[i]) + { + if (IsClientConnected(i)) + { + PrintCenterText(i, "Since you took over BOT, you will not retain weapons") + //TeleportWarning[i] = 1 + //new Float:iTargetOrigin[3] + //iTargetOrigin[0] = 0.0 + //iTargetOrigin[1] = 0.0 + //iTargetOrigin[2] = 0.0 + //NormalizeVector(iTargetOrigin, iTargetOrigin) + //TeleportEntity(i, iTargetOrigin, NULL_VECTOR, NULL_VECTOR) + } + CreateTimer(Weapon_Strip_Delay, StripWeapons, GetClientUserId(i)) + } + ClientTookover[i] = 0 + WrongTeamWarning[i] = 0 + } + gameround++ +} +public Action:Event_RoundFreezeEnd(Handle:Event, const String:name[], bool:dontBroadcast) +{ + //This entire routine is for debugging only + PrintToServer("Round %i -----------------------------------------------------------------------", gameround) + for (new i = 1; i <= MaxClients; i++) + { + if(!IsClientConnected(i) || !IsClientInGame(i) || IsClientObserver(i)) continue + new TempWeapon[5] + for (new ii = 0; ii <= 4; ii++) + { + TempWeapon[ii] = Client_GetWeaponBySlot(i, ii) + if (TempWeapon[ii] > -1) + { + new String:Weapon[32] + GetEdictClassname(TempWeapon[ii], Weapon, sizeof(Weapon)) + if (ii == 3) + { + new tnades = GetAllClientGrenades(i) + if (tnades) + { + PrintToServer("Nade report for %N:", i) + if (Nades[i][0]) PrintToServer("%i HE Nades", Nades[i][0]) + if (Nades[i][1]) PrintToServer("%i Flash Nades", Nades[i][1]) + if (Nades[i][2]) PrintToServer("%i Smoke Nades", Nades[i][2]) + PrintToServer ("Line 114 - Total = %i", tnades) + } + } + } + } + } +} +public Action:OnPlayerRunCmd(iClient, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon) +{ + if (!IsClientConnected(iClient) || !(buttons & IN_USE) || IsPlayerAlive(iClient) || !b2pEnabled || !IsClientObserver(iClient)) return Plugin_Continue + new iTarget = GetEntPropEnt(iClient, Prop_Send, "m_hObserverTarget") + new ClientCash = GetMoney(iClient) + if (IsValidClient(iTarget) && IsFakeClient(iTarget) && GetClientTeam(iClient) == GetClientTeam(iTarget) && ClientCash >= BotTakeverCost[iClient]) + { + //Get all of BOTs stats + new Float:iTargetOrigin[3], Float:iTargetAngles[3] + GetClientAbsOrigin(iTarget, iTargetOrigin) + GetClientAbsAngles(iTarget, iTargetAngles) + new iTargetHealth = GetClientHealth(iTarget) + new iTargetArmor = GetClientArmor(iTarget) + iTargetActiveWeapon = Client_GetActiveWeapon(iTarget) + for (new i = 0; i <= 1; i++) + { + iTargetWeapon[i] = Client_GetWeaponBySlot(iTarget, i) + if (iTargetWeapon[i] > -1) + { + Client_SetActiveWeapon(iTarget, iTargetWeapon[i]) + Client_GetActiveWeaponName(iTarget, iTargetActiveWeaponName, sizeof(iTargetActiveWeaponName)) + iTargetClip[i] = Weapon_GetPrimaryClip(iTargetWeapon[i]) + Client_GetWeaponPlayerAmmo(iTarget, iTargetActiveWeaponName, iTargetAmmo[i]) + } + else + { + iTargetClip[i] = 0 + iTargetAmmo[i] = 0 + } + } + //Check if target is alive one last time - fix for Known issue# 2 + if (!IsClientConnected(iTarget) || !IsValidClient(iTarget) || !IsPlayerAlive(iTarget)) + { + PrintHintText(iClient, "The BOT you tried to take over is no longer available for take over") + return Plugin_Continue + } + //Set all of humans stats, but not weapons + SetEntityHealth(iClient, iTargetHealth) + Client_SetArmor(iClient, iTargetArmor) + GetAllClientGrenades(iTarget) + CreateTimer(0.05, Give_iTargetWeaponsTo_iClient, iClient) + //Take control + bHideDeath[iTarget] = true + ClientTookover[iClient] = 1 + ClientSpecClient[iClient] = 0 + ClientCash = ClientCash - BotTakeverCost[iClient] + SetMoney(iClient, ClientCash) + BotTakeverCost[iClient] = BotTakeverCost[iClient] + BotTakeOverCostIncrement + //check for last player on team alive + new MyTeam = GetClientTeam(iClient) + new TeamMatesAlive = 0 + for (new i = 1; i <= MaxClients; i++) + { + if (!IsClientConnected(i) || i == iClient) continue + if (IsClientInGame(iClient) && IsClientInGame(i) && IsPlayerAlive(i) && MyTeam == GetClientTeam(i)) + { + TeamMatesAlive++ + } + } + new Handle:NoEndRoundHandle = FindConVar("mp_ignore_round_win_conditions") + if (TeamMatesAlive == 1) + { + SetConVarInt(NoEndRoundHandle, 1) + } + ForcePlayerSuicide(iTarget) + CS_RespawnPlayer(iClient) + TeleportEntity(iClient, iTargetOrigin, iTargetAngles, NULL_VECTOR) + SetConVarInt(NoEndRoundHandle, 0) + PrintToChatAll("%N took control of %N", iClient, iTarget) + return Plugin_Handled + } + return Plugin_Continue +} +public GetAllClientGrenades(client) +{ + Nades[client][0] = 0 + Nades[client][1] = 0 + Nades[client][2] = 0 + new offsNades = FindDataMapOffs(client, "m_iAmmo") + (11 * 4); + new granadesnr = GetEntData(client, offsNades) + new lastgranadesnr = 0 + if (granadesnr > lastgranadesnr) + { + // HE Nades + Nades[client][0] = granadesnr + lastgranadesnr = granadesnr + } + offsNades += 4 + granadesnr += GetEntData(client, offsNades) + if (granadesnr > lastgranadesnr) + { + // Flashbangs + Nades[client][1] = granadesnr - lastgranadesnr + lastgranadesnr = granadesnr + } + offsNades += 4 + granadesnr += GetEntData(client, offsNades) + if (granadesnr > lastgranadesnr) + { + // Smoke Nades + Nades[client][2] = granadesnr - lastgranadesnr + lastgranadesnr = granadesnr + } + return granadesnr +} +public OnClientPostAdminCheck(client) +{ + BotTakeverCost[client] = BotTakeOverStartingCost + ClientSpecClient[client] = 0 + ClientTookover[client] = 0 + WrongTeamWarning[client] = 0 +} +public Action:StripWeapons(Handle:timer, any:UserID) +{ + new client = GetClientOfUserId(UserID) + if (!client || !IsClientConnected(client)) return + Client_RemoveAllWeapons(client) + Client_GiveWeapon(client, "weapon_knife", true) +} +public Action:NewTarget(iClient, const String:cmd[], args) +{ + new iTarget = GetEntPropEnt(iClient, Prop_Send, "m_hObserverTarget") + if (!b2pEnabled || !IsValidClient(iTarget) || !IsClientObserver(iClient)) return Plugin_Continue; + CreateTimer(0.1, DisplayTakeOverMessage, iClient) + return Plugin_Continue; +} +public Action:DisplayTakeOverMessage(Handle:timer, any:iClient) +{ + if (!b2pEnabled || !IsClientConnected(iClient)) return Plugin_Continue + new ClientTeam = 0 + if (IsClientConnected(iClient)) + { + ClientTeam = GetClientTeam(iClient) + } + if (ClientTeam < 2) return Plugin_Continue + new iTarget = -1 + iTarget = GetEntPropEnt(iClient, Prop_Send, "m_hObserverTarget") + if (iTarget == -1) return Plugin_Continue + decl String:BOTName[64] + GetClientName(iTarget, BOTName, sizeof(BOTName)) + if (!IsValidClient(iTarget) || !IsClientObserver(iClient)) return Plugin_Continue + ClientSpecClient[iClient] = iTarget + new ClientCash = GetMoney(iClient) + if (ClientCash >= BotTakeverCost[iClient]) + { + if (IsFakeClient(iTarget)) + { + if (ClientTeam == GetClientTeam(iTarget)) + { + if (ClientCash >= BotTakeverCost[iClient]) + { + PrintHintText(iClient, "For $%i - Press the Use key [default E] to take control of %s", BotTakeverCost[iClient], BOTName) + return Plugin_Continue + } + else + { + PrintHintText(iClient, "You need $%i to take over any BOTs (the price increases each time you do)", BotTakeverCost[iClient]) + return Plugin_Continue + } + } + else + { + PrintHintText(iClient, "You can't take over BOTs that aren't on your team") + return Plugin_Continue + } + } + else + { + PrintHintText(iClient, "Spectate a BOT if you want to take over a BOT") + } + } + else + { + PrintHintText(iClient, "You need $%i to take over any BOTs (the price increases each time you do)", BotTakeverCost[iClient]) + return Plugin_Continue + } + return Plugin_Continue +} +public Action:Event_PlayerDeath(Handle:Event, const String:name[], bool:dontBroadcast) +{ + if (!b2pEnabled) return Plugin_Continue + new iClient = GetClientOfUserId(GetEventInt(Event, "userid")) + if (!IsClientConnected(iClient)) return Plugin_Continue + if (!IsFakeClient(iClient)) CreateTimer(6.75, DisplayTakeOverMessage, iClient) + for (new i = 1; i <= MaxClients; i++) + { + if (i < GetClientCount(true)) + { + new ClientCash = GetMoney(i) + if (IsClientConnected(iClient) && IsClientConnected(i) && IsClientInGame(i) && IsClientObserver(i) && ClientSpecClient[i] == iClient && ClientCash >= BotTakeverCost[i]) + { + PrintHintText(i, "%N died - You can't control dead BOTs", iClient) + ClientSpecClient[i] = 0 + } + } + } + if (!bHideDeath[iClient]) return Plugin_Continue + CreateTimer(0.2, tDestroyRagdoll, iClient) + return Plugin_Handled // Disable the killfeed notification for takeovers +} +public Action:tDestroyRagdoll(Handle:timer, any:iClient) +{ + new iRagdoll = GetEntPropEnt(iClient, Prop_Send, "m_hRagdoll") + bHideDeath[iClient] = false + if (iRagdoll < 0) return + AcceptEntityInput(iRagdoll, "kill"); +} +public Action:Give_iTargetWeaponsTo_iClient(Handle:timer, any:iClient) +{ + for (new i = 0; i <= 1; i++) + { + if (iTargetWeapon[i] != INVALID_ENT_REFERENCE) + { + Client_EquipWeapon(iClient, iTargetWeapon[i], false) + Client_SetActiveWeapon(iClient, iTargetWeapon[i]) + Client_GetActiveWeaponName(iClient, iTargetActiveWeaponName, sizeof(iTargetActiveWeaponName)) + Client_SetWeaponClipAmmo(iClient, iTargetActiveWeaponName, iTargetClip[i]) + Client_SetWeaponPlayerAmmo(iClient, iTargetActiveWeaponName, iTargetAmmo[i]) + } + Client_SetActiveWeapon(iClient, iTargetActiveWeapon) + } + if (Nades[iClient][0] > 0) Client_GiveWeapon(iClient, "weapon_hegrenade", false) + if (Nades[iClient][1] > 0) Client_GiveWeapon(iClient, "weapon_flashbang", false) + if (Nades[iClient][1] > 1) Client_GiveWeapon(iClient, "weapon_flashbang", false) + if (Nades[iClient][2] > 0) Client_GiveWeapon(iClient, "weapon_smokegrenade", false) +} +public cvar_b2p_enabledChange(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + b2pEnabled = GetConVarBool(cvar_b2p_enabled) +} +public CvarRoundRestartDelayChange(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + Round_Restart_Delay = GetConVarFloat(cvar_Round_Restart_Delay) + Weapon_Strip_Delay = Round_Restart_Delay - 0.3 +} +public CvarStartCostChange(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + BotTakeOverStartingCost = GetConVarInt(cvar_BotTakeOverStartingCost) +} +public CvarCostIncreaseChange(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + BotTakeOverCostIncrement = GetConVarInt(cvar_BotTakeOverCostIncrement) +} +stock FindRagdollClosestToEntity(iEntity, Float:fLimit) +{ + new iSearch = -1, + iReturn = -1; + new Float:fLowest = -1.0, + Float:fVectorDist, + Float:fEntityPos[3], + Float:fRagdollPos[3] + if (!IsValidEntity(iEntity)) return iReturn; + GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fEntityPos); + while ((iSearch = FindEntityByClassname(iSearch, "tf_ragdoll")) != -1) + { + GetEntPropVector(iSearch, Prop_Send, "m_vecRagdollOrigin", fRagdollPos); + fVectorDist = GetVectorDistance(fEntityPos, fRagdollPos); + if (fVectorDist < fLimit && (fVectorDist < fLowest || fLowest == -1.0)) + { + fLowest = fVectorDist + iReturn = iSearch + } + } + return iReturn +} +stock bool:IsValidClient(iClient) +{ + if (iClient <= 0 || iClient > MaxClients || !IsClientInGame(iClient)) return false + return true +} +public GetMoney(client) +{ + if (!IsClientConnected(client) || !IsClientInGame(client)) return 0 + if (g_iAccount != -1) + { + return GetEntData(client, g_iAccount) + } + else + { + return 0 + } +} +public SetMoney(client, amount) +{ + if (!IsClientConnected(client) || !IsClientInGame(client)) return + if (g_iAccount != -1) + { + SetEntData(client, g_iAccount, amount) + } +} +public EpicFail(String:FailReason[]) +{ + PrintToServer("[%s] - Fatal Error: %s", PLUGIN_NAME, FailReason) + SetFailState("[%s] - Fatal Error: %s", PLUGIN_NAME, FailReason) +} +//End of \ No newline at end of file -- cgit v1.2.3