summaryrefslogtreecommitdiff
path: root/sourcemod/scripting/bot2player_public.sp
diff options
context:
space:
mode:
authornavewindre <nw@moneybot.cc>2023-11-13 14:28:08 +0100
committernavewindre <nw@moneybot.cc>2023-11-13 14:28:08 +0100
commitda518fdc0f32839730ccdee8098b59c6f842d93f (patch)
treed6f856a6148c0b4d5819f88f068b7287b8044513 /sourcemod/scripting/bot2player_public.sp
parentbc678b10830cdaef64bcc592ca2524ebe0fcdc45 (diff)
ya
Diffstat (limited to 'sourcemod/scripting/bot2player_public.sp')
-rw-r--r--sourcemod/scripting/bot2player_public.sp462
1 files changed, 462 insertions, 0 deletions
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 <sourcemod>
+#include <cstrike>
+#include <smlib>
+
+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