diff --git a/README.md b/README.md index 4928c4f..beb0c3e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ All my sourcemod plugins... shitty probably * [L4D2FFKickProtection](#L4D2FFKickProtection) * [l4d2_ff_test](#l4d2_ff_test) * [CSGOTroll](#CSGOTroll) +* [l4d2_avoid_minigun](#l4d2_avoid_minigun) +* [l4d2_ai_minigun](#l4d2_ai_minigun) ## Descriptions ### csgo-knifehp @@ -89,3 +91,14 @@ Another joke plugin, with it configured, a victim will have a % chance their sho * `troll_shot_fail_percentage <0.0-1.0>` - percentage float (0.0 to 1.0) chance that victims' shots fail * `troll_targets ` - comma separated list of steamid64 targets (ex: STEAM_0:0:75141700) * `troll_shot_mode <0/1>` - 0 -> ALL Weapons, 1 -> AWP + +### l4d2_avoid_minigun +Makes the bots avoid standing infront/on top of the player that is using a minigun. It checks every 2.0 seconds if they are infront, then tells them to move to behind you. There is no configuration, all automatic. + +### l4d2_ai_minigun +Allows you to spawn a holdout type bot. This bot will spawn with a minigun, like louis in the passing. Supports all 8 characters. +Technically it is louis using minigun with a model change, but it works fine. + +Note: Sometimes bill model fails to spawn in, and is just invisible. +* **Commands:** + * `sm_spawn_minigun_bot ` - Spawns the ai bot infront of wherever you are looking. Can also use numbers (0-7). \ No newline at end of file diff --git a/plugins/l4d2_ai_minigun.smx b/plugins/l4d2_ai_minigun.smx new file mode 100644 index 0000000..0205150 Binary files /dev/null and b/plugins/l4d2_ai_minigun.smx differ diff --git a/plugins/l4d2_avoid_minigun.smx b/plugins/l4d2_avoid_minigun.smx new file mode 100644 index 0000000..1cc220b Binary files /dev/null and b/plugins/l4d2_avoid_minigun.smx differ diff --git a/scripting/l4d2_ai_minigun.sp b/scripting/l4d2_ai_minigun.sp new file mode 100644 index 0000000..d9fb5a7 --- /dev/null +++ b/scripting/l4d2_ai_minigun.sp @@ -0,0 +1,284 @@ +#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 +#include +//#include + +#define MODEL_MINIGUN "models/w_models/weapons/w_minigun.mdl" +#define MODEL_FRANCIS "models/survivors/survivor_biker.mdl" +#define MODEL_LOUIS "models/survivors/survivor_manager.mdl" +#define MODEL_ZOEY "models/survivors/survivor_teenangst.mdl" +#define MODEL_BILL "models/survivors/survivor_namvet.mdl" +#define MODEL_NICK "models/survivors/survivor_gambler.mdl" +#define MODEL_COACH "models/survivors/survivor_coach.mdl" +#define MODEL_ELLIS "models/survivors/survivor_mechanic.mdl" +#define MODEL_ROCHELLE "models/survivors/survivor_producer.mdl" + +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; + +public void OnPluginStart() +{ + EngineVersion g_Game = GetEngineVersion(); + if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) + { + SetFailState("This plugin is for L4D/L4D2 only."); + } + + RegAdminCmd("sm_spawn_minigun_bot", Command_SpawnAIBot, 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_SpawnAIBot(int client, int args) { + char arg1[16]; + if(args > 0) { + GetCmdArg(1, arg1, sizeof(arg1)); + char model[64]; + FindModelFromString(arg1, model, sizeof(model)); + + //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; + + if(!SpawnSurvivor(vPos, vAng, model, true)) { + 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; +} + +bool 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 false; + } + //set character type (7 = Louis) + DispatchKeyValue(entity, "character", "7"); + //on spawn, to kill spawner + //AcceptEntityInput(entity, "AddOutput"); + 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 false; + } + SetClientName(bot_client_id, "MinigunBot"); + TeleportEntity(bot_client_id, vPos, NULL_VECTOR, NULL_VECTOR); + + if(spawn_minigun && !SpawnMinigun(vAng, vPos)) { + LogError("Failed to spawn minigun for client #%d", bot_client_id); + KickClient(bot_client_id, "AIMinigun:MinigunSpawnFailure"); + return false; + } + TeleportEntity(bot_client_id, vPos, NULL_VECTOR, NULL_VECTOR); + SetEntityModel(bot_client_id, model); + CreateTimer(1.5, TimerMove, bot_user_id); + return true; +} + +stock bool FindModelFromString(const char str[16], char[] model, int modelStrSize) { + int possibleNumber = StringToInt(str, 10); + if(modelStrSize == 1 && possibleNumber <= 7 && possibleNumber >= 0) { + switch(possibleNumber) { + case 0: { + strcopy(model, modelStrSize, MODEL_NICK); + } case 1: { + strcopy(model, modelStrSize, MODEL_ELLIS); + } case 2: { + strcopy(model, modelStrSize, MODEL_COACH); + } case 3: { + strcopy(model, modelStrSize, MODEL_ROCHELLE); + } case 4: { + strcopy(model, modelStrSize, MODEL_BILL); + } case 5: { + strcopy(model, modelStrSize, MODEL_ZOEY); + } case 6: { + strcopy(model, modelStrSize, MODEL_FRANCIS); + } case 7: { + strcopy(model, modelStrSize, MODEL_LOUIS); + } + default: + return false; + } + return true; + }else{ + if(possibleNumber == 0) { + //try to parse str + if(StrEqual(str, "bill", false)) { + strcopy(model, modelStrSize, MODEL_BILL); + }else if(StrEqual(str, "zoey", false)) { + strcopy(model, modelStrSize, MODEL_ZOEY); + }else if(StrEqual(str, "francis", false)) { + strcopy(model, modelStrSize, MODEL_FRANCIS); + }else if(StrEqual(str, "louis", false)) { + strcopy(model, modelStrSize, MODEL_LOUIS); + }else if(StrEqual(str, "nick", false)) { + strcopy(model, modelStrSize, MODEL_NICK); + }else if(StrEqual(str, "ellis", false)) { + strcopy(model, modelStrSize, MODEL_ELLIS); + }else if(StrEqual(str, "rochelle", false)) { + strcopy(model, modelStrSize, MODEL_ROCHELLE); + }else if(StrEqual(str, "coach", false)) { + strcopy(model, modelStrSize, MODEL_COACH); + }else{ + return false; + } + return true; + } + } + return false; +} + +bool SpawnMinigun(const float vAng[3], const float vPos[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; + } +} + +stock bool TraceFilter(int entity, int contentsMask) { + if( entity <= MaxClients ) + return false; + return true; +} +stock bool GetGround(int client, float[3] vPos, float[3] vAng) { + GetClientAbsOrigin(client, vPos); + vAng = vPos; + vAng[2] += 5.0; + vPos[2] -= 500.0; + + Handle trace = TR_TraceRayFilterEx(vAng, vPos, MASK_SHOT, RayType_EndPoint, TraceFilter); + if(!TR_DidHit(trace)) + { + delete trace; + return false; + } + TR_GetEndPosition(vPos, trace); + delete trace; + + GetClientAbsAngles(client, vAng); + return true; +} + +g_iAvoidChar[MAXPLAYERS+1] = {-1,...}; +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 7: set = 1; // Francis + case 6: set = 0; // Louis + } + 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 TimerMove(Handle timer, any client) { + if((client = GetClientOfUserId(client))) { + //PrintToServer("client %d %N",client,client); + SetEntityMoveType(client, MOVETYPE_NONE); + TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, view_as({ 0.0, 0.0, 0.0 })); + } +} \ No newline at end of file diff --git a/scripting/l4d2_avoid_minigun.sp b/scripting/l4d2_avoid_minigun.sp new file mode 100644 index 0000000..b3f73ec --- /dev/null +++ b/scripting/l4d2_avoid_minigun.sp @@ -0,0 +1,116 @@ +#pragma semicolon 1 +#pragma newdecls required + +//#define DEBUG + +#define PLUGIN_NAME "L4D2 AI Avoid Minigun" +#define PLUGIN_DESCRIPTION "Makes the ai avoid being infront of a minigun in use" +#define PLUGIN_AUTHOR "jackzmc" +#define PLUGIN_VERSION "1.0" +#define PLUGIN_URL "" +#define PI 3.14159265358 +#define UNITS_SPAWN -120.0 + +#include +#include +//#include + +#define MODEL_MINIGUN "models/w_models/weapons/w_minigun.mdl" + +public Plugin myinfo = +{ + name = PLUGIN_NAME, + author = PLUGIN_AUTHOR, + description = PLUGIN_DESCRIPTION, + version = PLUGIN_VERSION, + url = PLUGIN_URL +}; + +public void OnPluginStart() +{ + EngineVersion g_Game = GetEngineVersion(); + if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) + { + SetFailState("This plugin is for L4D/L4D2 only."); + } + CreateTimer(2.0, CheckTimer, _, TIMER_REPEAT); +} + +public Action CheckTimer(Handle timer) { + for(int i = 1; i < MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == 2) { + bool usingMinigun = GetEntProp(i, Prop_Send, "m_usingMountedGun", 1) == 1; + bool usingMountedWeapon = GetEntProp(i, Prop_Send, "m_usingMountedWeapon", 1) == 1; + + if(usingMinigun || usingMountedWeapon) { + float pos[3], ang[3], finalPos[3], checkPos[3]; + GetClientAbsOrigin(i, pos); + GetClientEyeAngles(i, ang); + GetHorizontalPositionFromOrigin(pos, ang, 40.0, checkPos); + GetHorizontalPositionFromOrigin(pos, ang, UNITS_SPAWN, finalPos); + + for(int bot = 1; bot < MaxClients; bot++) { + if(IsClientConnected(bot) && IsFakeClient(bot) && GetClientTeam(bot) == 2) { + float botPos[3]; + GetClientAbsOrigin(bot, botPos); + + float center_distance = GetVectorDistance(checkPos, botPos); + + if(center_distance <= 70) { + //PrintHintTextToAll("Bot: %N | d=%f | d2=%f | Vector(%.2f,%.2f,%.2f)", bot, distance, center_distance, finalPos[0], finalPos[1], finalPos[2]); + //todo: only teleport once? + //TeleportEntity(bot, finalPos, NULL_VECTOR, NULL_VECTOR); + L4D2_RunScript("CommandABot({cmd=1,bot=GetPlayerFromUserID(%i),pos=Vector(%f,%f,%f)})", GetClientUserId(bot), finalPos[0], finalPos[1], finalPos[2]); + } + } + } + break; + } + } + } +} + + +stock void GetHorizontalPositionFromOrigin(const float pos[3], const float ang[3], float units, float finalPosition[3]) { + float theta = DegToRad(ang[1]); + finalPosition[0] = units * Cosine(theta) + pos[0]; + finalPosition[1] = units * Sine(theta) + pos[1]; + finalPosition[2] = pos[2]; +} +stock void GetHorizontalPositionFromClient(int client, float units, float finalPosition[3]) { + float pos[3], ang[3]; + GetClientEyeAngles(client, ang); + GetClientAbsOrigin(client, pos); + + float theta = DegToRad(ang[1]); + pos[0] += -150 * Cosine(theta); + pos[1] += -150 * Sine(theta); + finalPosition = pos; +} + +stock void L4D2_RunScript(const char[] sCode, any ...) { + static int iScriptLogic = INVALID_ENT_REFERENCE; + if(iScriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(iScriptLogic)) { + iScriptLogic = EntIndexToEntRef(CreateEntityByName("logic_script")); + if(iScriptLogic == INVALID_ENT_REFERENCE|| !IsValidEntity(iScriptLogic)) + SetFailState("Could not create 'logic_script'"); + + DispatchSpawn(iScriptLogic); + } + + static char sBuffer[512]; + VFormat(sBuffer, sizeof(sBuffer), sCode, 2); + + SetVariantString(sBuffer); + AcceptEntityInput(iScriptLogic, "RunScriptCode"); +} +stock void ShowDelayedHintToAll(const char[] format, any ...) { + char buffer[254]; + VFormat(buffer, sizeof(buffer), format, 2); + static int hintInt = 0; + if(hintInt >= 7) { + PrintHintTextToAll("%s",buffer); + hintInt = 0; + } + hintInt++; +} \ No newline at end of file