#include #include #include #include #include #pragma semicolon 1 #pragma newdecls required int g_iVelocity; int g_iCurrentTick; int g_iTicksSinceLastAttack; #define MAX_TURRETS 65 int g_iTurrets[MAX_TURRETS] = {INVALID_ENT_REFERENCE,...}; int g_iMarkers[MAX_TURRETS] = {INVALID_ENT_REFERENCE,...}; int g_iGunfire[MAX_TURRETS] = {INVALID_ENT_REFERENCE,...}; int g_iExplosion[MAX_TURRETS] = {INVALID_ENT_REFERENCE,...}; int g_iOwner[MAX_TURRETS]; bool g_bCanPlace[MAX_TURRETS]; bool g_bPlaced[MAX_TURRETS]; float g_fMaxRange; float g_fMinAccuracy; float g_fMaxAccuracy; float g_fDestroyRange; float g_fKnockback; int g_iDamage; int g_iTicksToSkip; //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Plugin myinfo = { name = "Turrets", author = "Neon", description = "", version = "1.0.0", url = "https://steamcommunity.com/id/n3ontm" }; //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnPluginStart() { //RegConsoleCmd("sm_turret", Command_Turret); RegAdminCmd("sm_turret", Command_Turret, ADMFLAG_RCON); HookEvent("round_end", OnRound); HookEvent("round_start", OnRound); ConVar cvar; HookConVarChange((cvar = CreateConVar("sm_turrets_max_range", "1500", "")), Cvar_MaxRange); g_fMaxRange = cvar.FloatValue; HookConVarChange((cvar = CreateConVar("sm_turrets_min_accuracy", "0.7", "")), Cvar_MinAccuracy); g_fMinAccuracy = cvar.FloatValue; HookConVarChange((cvar = CreateConVar("sm_turrets_max_accuracy", "0.99", "")), Cvar_MaxAccuracy); g_fMaxAccuracy = cvar.FloatValue; HookConVarChange((cvar = CreateConVar("sm_turrets_destroy_range", "100", "")), Cvar_DestroyRange); g_fDestroyRange = cvar.FloatValue; HookConVarChange((cvar = CreateConVar("sm_turrets_knockback", "20", "")), Cvar_Knockback); g_fKnockback = cvar.FloatValue; HookConVarChange((cvar = CreateConVar("sm_turrets_damage", "10", "")), Cvar_Damage); g_iDamage = cvar.IntValue; HookConVarChange((cvar = CreateConVar("sm_turrets_ticks_to_skip", "3", "")), Cvar_TicksToSkip); // to decrease CPU usage g_iTicksToSkip = cvar.IntValue; delete cvar; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnMapStart() { g_iVelocity = FindDataMapInfo(0, "m_vecAbsVelocity"); PrecacheModel("models/unloze/dronegun/dronegun.mdl"); AddFileToDownloadsTable("models/unloze/dronegun/dronegun.dx90.vtx"); AddFileToDownloadsTable("models/unloze/dronegun/dronegun.dx80.vtx"); AddFileToDownloadsTable("models/unloze/dronegun/dronegun.mdl"); AddFileToDownloadsTable("models/unloze/dronegun/dronegun.phy"); AddFileToDownloadsTable("models/unloze/dronegun/dronegun.sw.vtx"); AddFileToDownloadsTable("models/unloze/dronegun/dronegun.vvd"); AddFileToDownloadsTable("materials/models/unloze/dronegun/dronegun.vmt"); AddFileToDownloadsTable("materials/models/unloze/dronegun/dronegun.vtf"); AddFileToDownloadsTable("materials/models/unloze/dronegun/dronegun_laser.vmt"); AddFileToDownloadsTable("materials/models/unloze/dronegun/dronegun_laser.vtf"); AddFileToDownloadsTable("materials/models/unloze/dronegun/dronegun_selfillum.vtf"); AddFileToDownloadsTable("materials/models/unloze/weapons/w_models/w_mach_m249para/m249.vmt"); AddFileToDownloadsTable("materials/models/unloze/weapons/w_models/w_mach_m249para/m249.vtf"); AddFileToDownloadsTable("materials/models/unloze/weapons/w_models/w_mach_m249para/m249_exponent.vtf"); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnPluginEnd() { for (int i = 0; i < MAX_TURRETS; i++) { if (g_iTurrets[i] == INVALID_ENT_REFERENCE) continue; ClearTurret(i); } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnRound(Event hEvent, const char[] sEvent, bool bDontBroadcast) { for (int i = 0; i < MAX_TURRETS; i++) { g_iTurrets[i] = INVALID_ENT_REFERENCE; g_iMarkers[i] = INVALID_ENT_REFERENCE; g_iGunfire[i] = INVALID_ENT_REFERENCE; g_iExplosion[i] = INVALID_ENT_REFERENCE; g_iOwner[i] = 0; g_bCanPlace[i] = false; g_bPlaced[i] = false; } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public Action Command_Turret(int client, int args) { if (!(IsPlayerAlive(client) && ZR_IsClientHuman(client))) { ReplyToCommand(client, "[ZR] You need to be human to use this command."); return Plugin_Handled; } // Clear old turret for (int j = 0; j < MAX_TURRETS; j++) { if ((g_iOwner[j] != 0) && (g_iOwner[j] == client)) { ClearTurret(j); ReplyToCommand(client, "[ZR] Turret Removed."); return Plugin_Handled; } } // find free index int i; for (i = 0; i < MAX_TURRETS; i++) { if (g_iTurrets[i] == INVALID_ENT_REFERENCE) break; if (i == (MAX_TURRETS -1)) { ReplyToCommand(client, "[ZR] Too many turrets active already."); return Plugin_Handled; } } float vecOrigin[3]; GetClientAbsOrigin(client, vecOrigin); g_iTurrets[i] = CreateEntityAtOrigin("prop_dynamic_override", vecOrigin); SetEntityModel(g_iTurrets[i], "models/unloze/dronegun/dronegun.mdl"); DispatchKeyFormat(g_iTurrets[i], "targetname", "turret_%d", i); DispatchKeyFormat(g_iTurrets[i], "solid", "0"); DispatchKeyFormat(g_iTurrets[i], "modelscale", "1.0"); DispatchKeyFormat(g_iTurrets[i], "disableshadows", "1"); DispatchKeyFormat(g_iTurrets[i], "disablereceiveshadows", "1"); DispatchKeyFormat(g_iTurrets[i], "disablebonefollowers", "1"); DispatchKeyFormat(g_iTurrets[i], "OnUser1", "gunfire_%d,Kill,,0,1", i); DispatchKeyFormat(g_iTurrets[i], "OnUser1", "marker_%d,Kill,,0,1", i); DispatchKeyFormat(g_iTurrets[i], "OnUser1", "!self,Kill,,0,1"); DispatchKeyFormat(g_iTurrets[i], "OnUser2", "explosion_%d,Explode,,0,1", i); SpawnAndActivate(g_iTurrets[i]); //traget for env_gunfire to aim at g_iMarkers[i] = CreateEntityAtOrigin("path_track", vecOrigin); DispatchKeyFormat(g_iMarkers[i], "targetname", "marker_%d", i); SpawnAndActivate(g_iMarkers[i]); vecOrigin[2] += 42.0; // "middle" of turret g_iGunfire[i] = CreateEntityAtOrigin("env_gunfire", vecOrigin); DispatchKeyFormat(g_iGunfire[i], "targetname", "gunfire_%d", i); DispatchKeyFormat(g_iGunfire[i], "target", "marker_%d", i); DispatchKeyFormat(g_iGunfire[i], "StartDisabled", "1"); DispatchKeyFormat(g_iGunfire[i], "spread", "0"); DispatchKeyFormat(g_iGunfire[i], "collisions", "1"); DispatchKeyFormat(g_iGunfire[i], "rateoffire", "10"); SpawnAndActivate(g_iGunfire[i]); ParentToEntity(g_iGunfire[i], g_iTurrets[i]); g_iExplosion[i] = CreateEntityAtOrigin("env_explosion", vecOrigin); DispatchKeyFormat(g_iExplosion[i], "targetname", "explosion_%d", i); DispatchKeyFormat(g_iExplosion[i], "fireballsprite", "sprites/zerogxplode.spr"); DispatchKeyFormat(g_iExplosion[i], "rendermode", "0"); DispatchKeyFormat(g_iExplosion[i], "iMagnitude", "100"); DispatchKeyFormat(g_iExplosion[i], "spawnflags", "1"); SpawnAndActivate(g_iExplosion[i]); ParentToEntity(g_iExplosion[i], g_iTurrets[i]); g_iOwner[i] = client; ReplyToCommand(client, "[ZR] Press E to place a turret."); return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnGameFrame() // Tick Turrets { g_iCurrentTick++; g_iTicksSinceLastAttack++; if (g_iCurrentTick < g_iTicksToSkip) return; g_iCurrentTick = 0; bool bAttack = false; if (g_iTicksSinceLastAttack > 15) bAttack = true; for (int i = 0; i < MAX_TURRETS; i++) { if (g_iTurrets[i] == INVALID_ENT_REFERENCE) continue; if (!g_bPlaced[i]) // not placed yet { PlaceTurret(i); continue; } float vecOriginClient[3]; float vecOriginTurret[3]; GetEntPropVector(g_iTurrets[i], Prop_Send, "m_vecOrigin", vecOriginTurret); vecOriginTurret[2] += 42.0; int iNearestZombie; float fNearestDistance; for (int client = 1; client <= MaxClients; client++) { if (!IsClientInGame(client) || !IsPlayerAlive(client) || !ZR_IsClientZombie(client)) continue; GetClientAbsOrigin(client, vecOriginClient); vecOriginClient[2] += 50.0; //aim for chest Handle hTraceRay = TR_TraceRayFilterEx(vecOriginClient, vecOriginTurret, MASK_ALL, RayType_EndPoint, TraceEntityFilter, g_iTurrets[i]); if(TR_DidHit(hTraceRay)) { delete hTraceRay; continue; } delete hTraceRay; float vecVector[3]; float vecAngles[3]; MakeVectorFromPoints(vecOriginTurret, vecOriginClient, vecVector); GetVectorAngles(vecVector, vecAngles); if (!((vecAngles[0] <= 45.0) || (vecAngles[0] >= 315.0))) continue; float fDistance = GetVectorLength(vecVector, false); if ((fDistance < fNearestDistance) || (iNearestZombie == 0)) { iNearestZombie = client; fNearestDistance = fDistance; } } if (iNearestZombie == 0) // no target found continue; // ZM too close, destroy turret if (fNearestDistance <= g_fDestroyRange) { ClearTurret(i, true); continue; } float vecVector[3]; float vecAngles[3]; GetClientAbsOrigin(iNearestZombie, vecOriginClient); vecOriginClient[2] += 50.0; //aim for chest MakeVectorFromPoints(vecOriginTurret, vecOriginClient, vecVector); GetVectorAngles(vecVector, vecAngles); float fA = ((vecAngles[1]+180.0)/360.0); if (fA > 1.0) fA -= 1.0; float fB; if (vecAngles[0] <= 45.0) { fB = ((1.0/90.0) * vecAngles[0]) + 0.5; } else if (vecAngles[0] >= 315.0) { fB = ((1.0/90.0) * (vecAngles[0] - 315.0)) + 0.0; } // why the fuck can i only set one of those angles???? //SetEntPropFloat(g_iTurrets[i], Prop_Data, "m_flPoseParameter", fB, 1); //SetEntPropFloat(g_iTurrets[i], Prop_Send, "m_flCycle", 1.0); SetEntPropFloat(g_iTurrets[i], Prop_Data, "m_flPoseParameter", fA, 0); SetEntPropFloat(g_iTurrets[i], Prop_Send, "m_flCycle", 0.0); // randomly spread the bullets vecOriginClient[0] += GetRandomFloat(-15.0, 15.0); vecOriginClient[1] += GetRandomFloat(-15.0, 15.0); vecOriginClient[2] += GetRandomFloat(-20.0, 20.0); TeleportEntity(g_iMarkers[i], vecOriginClient, NULL_VECTOR, NULL_VECTOR); if (bAttack) { g_iTicksSinceLastAttack = 0; // check for max range float fDistance = GetVectorDistance(vecOriginClient, vecOriginTurret, false); if (fDistance > g_fMaxRange) { AcceptEntityInput(g_iGunfire[i], "Disable"); continue; } AcceptEntityInput(g_iGunfire[i], "Enable"); // Accuracy float fAccuracy = (((-(g_fMaxAccuracy - g_fMinAccuracy))/g_fMaxRange) * fDistance) + g_fMaxAccuracy; float R = GetRandomFloat(0.0, 100.0); if (FloatCompare(R, fAccuracy) == -1) continue; // DMG int iHealth = GetClientHealth(iNearestZombie); if(iHealth > g_iDamage) SetEntityHealth(iNearestZombie, iHealth - g_iDamage); else ForcePlayerSuicide(iNearestZombie); // KB MakeVectorFromPoints(vecOriginTurret, vecOriginClient, vecVector); NormalizeVector(vecVector, vecVector); ScaleVector(vecVector, g_fKnockback); float fClientVelocity[3]; GetEntDataVector(iNearestZombie, g_iVelocity, fClientVelocity); AddVectors(fClientVelocity, vecVector, fClientVelocity); SetEntDataVector(iNearestZombie, g_iVelocity, fClientVelocity); } } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void PlaceTurret(int i) { if (g_iTurrets[i] == INVALID_ENT_REFERENCE) // no turret return; int client = g_iOwner[i]; if (!IsValidClient(client)) { ClearTurret(i); return; } if (!(IsPlayerAlive(client) && ZR_IsClientHuman(client))) { ClearTurret(i); return; } float vecEyeAngles[3]; float vecEyeOrigin[3]; GetClientEyeAngles(client, vecEyeAngles); GetClientEyePosition(client, vecEyeOrigin); Handle hTraceRay = TR_TraceRayFilterEx(vecEyeOrigin, vecEyeAngles, MASK_ALL, RayType_Infinite, TraceEntityFilter, client); g_bCanPlace[i] = false; if(TR_DidHit(hTraceRay)) { float vecEndPosAim[3]; TR_GetEndPosition(vecEndPosAim, hTraceRay); float vecDown[3] = {90.0, 0.0, 0.0}; delete hTraceRay; hTraceRay = TR_TraceRayFilterEx(vecEndPosAim, vecDown, MASK_ALL, RayType_Infinite, TraceEntityFilter, client); if(TR_DidHit(hTraceRay)) { float vecEndPosDown[3]; TR_GetEndPosition(vecEndPosDown, hTraceRay); TeleportEntity(g_iTurrets[i], vecEndPosDown, NULL_VECTOR, NULL_VECTOR); if (GetVectorDistance(vecEyeOrigin, vecEndPosDown, false) > 500) // maximum distance to place a turret { delete hTraceRay; SetEntityRenderColor(g_iTurrets[i], 255, 0, 0, 0); return; } vecEndPosDown[2] += 5.0; vecEndPosAim[2] += 5.0; float vecMins[3] = {-30.0, -30.0, 0.0}; float vecMaxs[3] = {30.0, 30.0, 63.0}; delete hTraceRay; hTraceRay = TR_TraceHullFilterEx(vecEndPosDown, vecEndPosAim, vecMins, vecMaxs, MASK_ALL, TraceEntityFilter, client); if(!TR_DidHit(hTraceRay)) g_bCanPlace[i] = true; } } delete hTraceRay; if(g_bCanPlace[i]) SetEntityRenderColor(g_iTurrets[i], 0, 255, 0, 0); else SetEntityRenderColor(g_iTurrets[i], 255, 0, 0, 0); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void OnPlayerRunCmdPost(int client, int buttons) { for (int i = 0; i < MAX_TURRETS; i++) { if (g_iTurrets[i] == INVALID_ENT_REFERENCE) // no turret continue; if (g_bPlaced[i]) // already placed continue; if(!g_bCanPlace[i]) // cant be placed atm continue; if (g_iOwner[i] == client) { if (buttons & IN_USE) { SetEntityRenderColor(g_iTurrets[i], 255, 255, 255, 0); //g_iOwner[i] = 0; g_bPlaced[i] = true; } break; } } } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock bool TraceEntityFilter(int entity, int contentsMask, int turret) { if ((entity == turret) || (0 <= entity <= MaxClients)) return false; return true; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock int CreateEntityAtOrigin(const char[] classname, const float origin[3]) { int entity = CreateEntityByName(classname); TeleportEntity(entity, origin, NULL_VECTOR, NULL_VECTOR); return entity; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock bool DispatchKeyFormat(int entity, const char[] key, const char[] value, any ...) { char buffer[1024]; VFormat(buffer, sizeof(buffer), value, 4); DispatchKeyValue(entity, key, buffer); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock void SpawnAndActivate(int entity) { DispatchSpawn(entity); ActivateEntity(entity); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock void ParentToEntity(int entity, int parent) { SetVariantString("!activator"); AcceptEntityInput(entity, "SetParent", parent, parent); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- void ClearTurret(int index, bool bExplode = false) { if (bExplode) AcceptEntityInput(g_iTurrets[index], "FireUser2"); AcceptEntityInput(g_iTurrets[index], "FireUser1"); g_iTurrets[index] = INVALID_ENT_REFERENCE; g_iMarkers[index] = INVALID_ENT_REFERENCE; g_iGunfire[index] = INVALID_ENT_REFERENCE; g_iExplosion[index] = INVALID_ENT_REFERENCE; g_iOwner[index] = 0; g_bCanPlace[index] = false; g_bPlaced[index] = false; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- stock bool IsValidClient(int client, bool nobots = true) { if (client <= 0 || client > MaxClients || !IsClientConnected(client) || (nobots && IsFakeClient(client))) return false; return IsClientInGame(client); } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_MaxRange(ConVar convar, const char[] oldValue, const char[] newValue) { g_fMaxRange = convar.FloatValue; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_MinAccuracy(ConVar convar, const char[] oldValue, const char[] newValue) { g_fMinAccuracy = convar.FloatValue; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_MaxAccuracy(ConVar convar, const char[] oldValue, const char[] newValue) { g_fMaxAccuracy = convar.FloatValue; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_DestroyRange(ConVar convar, const char[] oldValue, const char[] newValue) { g_fDestroyRange = convar.FloatValue; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_Knockback(ConVar convar, const char[] oldValue, const char[] newValue) { g_fKnockback = convar.FloatValue; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_Damage(ConVar convar, const char[] oldValue, const char[] newValue) { g_iDamage = convar.IntValue; } //---------------------------------------------------------------------------------------------------- // Purpose: //---------------------------------------------------------------------------------------------------- public void Cvar_TicksToSkip(ConVar convar, const char[] oldValue, const char[] newValue) { g_iTicksToSkip = convar.IntValue; }