mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 23:03:20 +00:00
307 lines
9 KiB
SourcePawn
307 lines
9 KiB
SourcePawn
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
//#define DEBUG
|
|
|
|
#define PLUGIN_NAME "l4d2 ai minigun"
|
|
#define PLUGIN_DESCRIPTION ""
|
|
#define PLUGIN_AUTHOR "jackzmc"
|
|
#define PLUGIN_VERSION "1.0"
|
|
#define PLUGIN_URL ""
|
|
|
|
#include <sourcemod>
|
|
#include <sdktools>
|
|
#include "jutils.inc"
|
|
#include <left4dhooks>
|
|
//#include <sdkhooks>
|
|
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = PLUGIN_NAME,
|
|
author = PLUGIN_AUTHOR,
|
|
description = PLUGIN_DESCRIPTION,
|
|
version = PLUGIN_VERSION,
|
|
url = PLUGIN_URL
|
|
};
|
|
|
|
int g_iSurvivors[MAXPLAYERS+1], g_iLastSpawnClient, g_iAvoidChar[MAXPLAYERS+1] = {-1,...};
|
|
|
|
public void OnPluginStart() {
|
|
EngineVersion g_Game = GetEngineVersion();
|
|
if(g_Game != Engine_Left4Dead2) {
|
|
SetFailState("This plugin is for L4D2 only.");
|
|
}
|
|
|
|
RegAdminCmd("sm_ai_holdout", Command_SpawnHoldoutBot, ADMFLAG_ROOT);
|
|
RegAdminCmd("sm_ai_minigun", Command_SpawnMinigunBot, ADMFLAG_ROOT);
|
|
RegAdminCmd("sm_ai_remove_far", Command_RemoveFar, ADMFLAG_ROOT);
|
|
}
|
|
|
|
public void OnMapStart() {
|
|
PrecacheModel(MODEL_MINIGUN);
|
|
PrecacheModel(MODEL_LOUIS);
|
|
PrecacheModel(MODEL_ZOEY);
|
|
PrecacheModel(MODEL_BILL);
|
|
PrecacheModel(MODEL_FRANCIS);
|
|
}
|
|
|
|
public void OnClientPutInServer(int client) {
|
|
if( g_iLastSpawnClient == -1)
|
|
{
|
|
g_iSurvivors[client] = GetClientUserId(client);
|
|
g_iLastSpawnClient = GetClientUserId(client);
|
|
}
|
|
}
|
|
|
|
public Action Command_RemoveFar(int client, int args) {
|
|
for(int bot = 1; bot < MaxClients; bot++) {
|
|
if(IsClientConnected(bot) && IsClientInGame(bot) && GetClientTeam(bot) == 4 && IsFakeClient(bot)) {
|
|
char name[64];
|
|
GetClientName(bot, name, sizeof(name));
|
|
//Only work on <Holdout/Minigun>Bot's
|
|
if(StrContains(name, "HoldoutBot", true) || StrContains(name, "MinigunBot", true)) {
|
|
float botPos[3];
|
|
GetClientAbsOrigin(bot, botPos);
|
|
bool isClose = false;
|
|
//Loop all players, if distance is less than 750, break out of loop
|
|
for(int i = 1; i < MaxClients; i++) {
|
|
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && !IsFakeClient(i)) {
|
|
float playerPos[3];
|
|
GetClientAbsOrigin(i, playerPos);
|
|
|
|
float distance = GetVectorDistance(playerPos, botPos);
|
|
if(distance <= 750) {
|
|
isClose = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!isClose) {
|
|
KickClient(bot);
|
|
ReplyToCommand(client, "Removed %N", bot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Action Command_SpawnMinigunBot(int client, int args) {
|
|
char arg1[16];
|
|
if(args > 0) {
|
|
GetCmdArg(1, arg1, sizeof(arg1));
|
|
char model[64];
|
|
if(!FindSurvivorModel(arg1, model, sizeof(model))) {
|
|
LogError("Could not find a survivor model.");
|
|
ReplyToCommand(client, "Could not find that survivor.");
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
//get ground:
|
|
float vPos[3], vAng[3];
|
|
if(!GetGround(client, vPos, vAng)) {
|
|
LogError("Failed to find ground for survivor");
|
|
ReplyToCommand(client, "Could not find a suitable ground location to spawn survivor.");
|
|
return Plugin_Handled;
|
|
}
|
|
//make sure spawns a little above
|
|
vPos[2] += 1.0;
|
|
|
|
int survivor = SpawnSurvivor(vPos, vAng, model, true);
|
|
if(survivor > 0) {
|
|
GiveClientWeaponLasers(survivor, "rifle_ak47");
|
|
}else{
|
|
ReplyToCommand(client, "Failed to spawn survivor.");
|
|
}
|
|
}else{
|
|
ReplyToCommand(client, "Usage: sm_spawn_minigun_bot <4=Bill, 5=Zoey, 6=Francis, 7=Louis>");
|
|
}
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_SpawnHoldoutBot(int client, int args) {
|
|
char arg1[16];
|
|
if(args > 0) {
|
|
GetCmdArg(1, arg1, sizeof(arg1));
|
|
char model[64];
|
|
int survivorId = GetSurvivorId(arg1);
|
|
if(survivorId == -1) {
|
|
ReplyToCommand(client, "That is not a valid survivor.");
|
|
return Plugin_Handled;
|
|
}
|
|
if(!GetSurvivorModel(survivorId, model, sizeof(model))) {
|
|
LogError("Could not find a survivor model.");
|
|
ReplyToCommand(client, "Could not find that survivor.");
|
|
return Plugin_Handled;
|
|
}
|
|
//get ground:
|
|
float vPos[3], vAng[3];
|
|
if(!GetGround(client, vPos, vAng)) {
|
|
LogError("Failed to find ground for survivor");
|
|
ReplyToCommand(client, "Could not find a suitable ground location to spawn survivor.");
|
|
return Plugin_Handled;
|
|
}
|
|
//make sure spawns a little above
|
|
vPos[2] += 1.0;
|
|
|
|
char wpn[64];
|
|
if(args > 1) {
|
|
GetCmdArg(2, wpn, sizeof(wpn));
|
|
}else {
|
|
wpn = "rifle_ak47";
|
|
}
|
|
|
|
int survivor = SpawnSurvivor(vPos, vAng, model, false);
|
|
if(survivor > 0) {
|
|
GiveClientWeaponLasers(survivor, wpn);
|
|
SetEntProp(survivor, Prop_Send, "m_survivorCharacter", survivorId);
|
|
}else{
|
|
ReplyToCommand(client, "Failed to spawn survivor.");
|
|
}
|
|
}else{
|
|
ReplyToCommand(client, "Usage: sm_spawn_minigun_bot <4=Bill, 5=Zoey, 6=Francis, 7=Louis> [weapon]");
|
|
}
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
///////////////////////////////////////////
|
|
//
|
|
//STOCKS
|
|
//
|
|
///////////////////////////////////////////
|
|
|
|
//Taken from https://forums.alliedmods.net/showthread.php?p=1741099 and modified slightly (and documented)
|
|
stock int SpawnSurvivor(const float vPos[3], const float vAng[3], const char[] model, bool spawn_minigun) {
|
|
int entity = CreateEntityByName("info_l4d1_survivor_spawn");
|
|
if( entity == -1 ) {
|
|
LogError("Failed to create \"info_l4d1_survivor_spawn\"");
|
|
return -1;
|
|
}
|
|
//on spawn, to kill spawner
|
|
//AcceptEntityInput(entity, "AddOutput");
|
|
DispatchKeyValue(entity, "character", "7");
|
|
AcceptEntityInput(entity, "Kill");
|
|
|
|
//teleport spawner to valid spot & spawn it
|
|
TeleportEntity(entity, vPos, vAng, NULL_VECTOR);
|
|
DispatchSpawn(entity);
|
|
|
|
//Tell spawner to spawn survivor
|
|
g_iLastSpawnClient = -1;
|
|
AvoidCharacter(7, true);
|
|
AcceptEntityInput(entity, "SpawnSurvivor");
|
|
AvoidCharacter(7, false);
|
|
|
|
|
|
//remove reference to last spawn id
|
|
int bot_user_id = g_iLastSpawnClient, bot_client_id;
|
|
g_iLastSpawnClient = -1;
|
|
if( bot_user_id <= 0 || (bot_client_id = GetClientOfUserId(bot_user_id)) <= 0 )
|
|
{
|
|
LogError("Failed to match survivor, did they not spawn? [%d/%d]", bot_user_id, bot_client_id);
|
|
return -1;
|
|
}
|
|
if(spawn_minigun && !SpawnMinigun(vPos, vAng)) {
|
|
LogError("Failed to spawn minigun for client #%d", bot_client_id);
|
|
KickClient(bot_client_id, "AIMinigun:MinigunSpawnFailure");
|
|
return -1;
|
|
}
|
|
SetClientName(bot_client_id, spawn_minigun ? "MinigunBot" : "HoldoutBot");
|
|
ChangeClientTeam(bot_client_id, 4);
|
|
|
|
CreateTimer(0.1, Timer_Move, bot_user_id);
|
|
TeleportEntity(bot_client_id, vPos, NULL_VECTOR, NULL_VECTOR);
|
|
|
|
SetEntityModel(bot_client_id, model); //set entity model to custom survivor model
|
|
CreateTimer(6.0, spawn_minigun ? Timer_MoveMinigun : Timer_Move, bot_user_id);
|
|
return bot_client_id;
|
|
}
|
|
//Taken from https://forums.alliedmods.net/showthread.php?p=1741099
|
|
stock bool SpawnMinigun(const float vPos[3], const float vAng[3]) {
|
|
float vDir[3], newPos[3];
|
|
GetAngleVectors(vAng, vDir, NULL_VECTOR, NULL_VECTOR);
|
|
vDir[0] = vPos[0] + (vDir[0] * 50);
|
|
vDir[1] = vPos[1] + (vDir[1] * 50);
|
|
vDir[2] = vPos[2] + 20.0;
|
|
newPos = vDir;
|
|
newPos[2] -= 40.0;
|
|
|
|
Handle trace = TR_TraceRayFilterEx(vDir, newPos, MASK_SHOT, RayType_EndPoint, TraceFilter);
|
|
if(TR_DidHit(trace)) {
|
|
TR_GetEndPosition(vDir, trace);
|
|
|
|
int minigun = CreateEntityByName("prop_mounted_machine_gun");
|
|
minigun = EntIndexToEntRef(minigun);
|
|
SetEntityModel(minigun, MODEL_MINIGUN);
|
|
DispatchKeyValue(minigun, "targetname", "louis_holdout");
|
|
DispatchKeyValueFloat(minigun, "MaxPitch", 360.00);
|
|
DispatchKeyValueFloat(minigun, "MinPitch", -360.00);
|
|
DispatchKeyValueFloat(minigun, "MaxYaw", 90.00);
|
|
newPos[2] += 0.1;
|
|
TeleportEntity(minigun, vDir, vAng, NULL_VECTOR);
|
|
DispatchSpawn(minigun);
|
|
delete trace;
|
|
return true;
|
|
}else{
|
|
LogError("Spawn minigun trace failure");
|
|
delete trace;
|
|
return false;
|
|
}
|
|
}
|
|
void AvoidCharacter(int type, bool avoid) {
|
|
for( int i = 1; i <= MaxClients; i++ )
|
|
{
|
|
if( IsClientInGame(i) && (GetClientTeam(i) == 2 || GetClientTeam(i) == 4) )
|
|
{
|
|
if( avoid )
|
|
{
|
|
// Save character type
|
|
g_iAvoidChar[i] = GetEntProp(i, Prop_Send, "m_survivorCharacter");
|
|
int set;
|
|
switch( type )
|
|
{
|
|
case 4: set = 3; // Bill
|
|
case 5: set = 2; // Zoey
|
|
case 6: set = 1; // Francis
|
|
case 7: set = 0; // Louis
|
|
default: return;
|
|
}
|
|
SetEntProp(i, Prop_Send, "m_survivorCharacter", set);
|
|
} else {
|
|
// Restore player type
|
|
if( g_iAvoidChar[i] != -1 )
|
|
{
|
|
SetEntProp(i, Prop_Send, "m_survivorCharacter", g_iAvoidChar[i]);
|
|
g_iAvoidChar[i] = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!avoid) {
|
|
for( int i = 1; i <= MAXPLAYERS; i++ )
|
|
g_iAvoidChar[i] = -1;
|
|
}
|
|
}
|
|
|
|
Action Timer_Move(Handle timer, any client) {
|
|
if((client = GetClientOfUserId(client))) {
|
|
//PrintToServer("client %d %N",client,client);
|
|
L4D2_SetPlayerSurvivorGlowState(client, false);
|
|
L4D2_RemoveEntityGlow(client);
|
|
// SetEntityMoveType(client, MOVETYPE_NONE);
|
|
SetEntProp(client, Prop_Send, "m_fFlags", GetEntProp(client, Prop_Send, "m_fFlags") & ~FL_FROZEN);
|
|
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
|
|
}
|
|
}
|
|
|
|
Action Timer_MoveMinigun(Handle timer, any client) {
|
|
if((client = GetClientOfUserId(client))) {
|
|
L4D2_RemoveEntityGlow(client);
|
|
L4D2_SetPlayerSurvivorGlowState(client, false);
|
|
SetEntityMoveType(client, MOVETYPE_NONE);
|
|
// SetEntProp(client, Prop_Send, "m_fFlags", GetEntProp(client, Prop_Send, "m_fFlags") & ~FL_FROZEN);
|
|
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, view_as<float>({ 0.0, 0.0, 0.0 }));
|
|
}
|
|
}
|
|
|