diff --git a/README.md b/README.md index 95ba063..f59cf31 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # sourcemod-plugins -This is a collection of all the sourcemod plugins I've created, most are just used for my own servers and some for very specific needs. +This is a collection of sourcemod plugins, most are used on my servers. The majority of the plugins are created by me, but some are modifications of other plugins. +Some of the plugins / changes are very specific, but may be useful to someone. -Not always the latest versions, if you have any interest with plugins I can make sure to upload the latest. +Not always the latest versions. If you have any interest with a plugin, I can make sure to upload the latest. Useful things: 1. Netprop viewer https://jackz.me/netprops/l4d2 @@ -16,33 +17,34 @@ Useful things: * [l4d2-manual-director](#l4d2-manual-director) - Spawn specials on demand via director or at your cursor * [l4d2-info-cmd](#l4d2-info-cmd) - Prints a full state of all survivors, useful for external information * [AutoWarpBot](#autowarpbot) - Abandoned - * [L4D2FFKickProtection](#l4d2ffkickprotection) - Prevents friendly firing from players being voted off and admins from being kicked - * [l4d2_avoid_minigun](#l4d2_avoid_minigun) - Makes bots avoid being infront of any in use miniguns. Useful for spawned miniguns + * [L4D2FFKickProtection](#l4d2ffkickprotection) - Prevents players being voted off from friendly firing and prevents admins from being kicked + * [l4d2_avoid_minigun](#l4d2_avoid_minigun) - Makes bots avoid being infront of any in-use miniguns. Useful for spawned miniguns * [l4d2_ai_minigun](#l4d2_ai_minigun) - Based off [Silver's Survivor Bot Holdout plugin](https://forums.alliedmods.net/showthread.php?p=1741099), allows you to spawn survivor bots but with no limit. * [L4D2Tools](#l4d2tools) - A collection of utilities, mostly just used with [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) and the /model command * [l4d2_swarm](#l4d2_swarm) - Uses vscript RushVictim to make all zombies target a player, like a more subtle vomitplayer * [l4d2_feedthetrolls](#l4d2_feedthetrolls) - Full collection of tools to troll your friends or troll the trolls - * [l4d2_autobotcrown](#l4d2_autobotcrown) - Bots will auto crown - * [l4d2_extraplayeritems](#l4d2_extraplayeritems) - Includes tons of utilities for 5+ games, such as 5+ player hud, extra kit spawning, and more + * [l4d2_autobotcrown](#l4d2_autobotcrown) - Bots will auto crown + * [l4d2_extraplayeritems](#l4d2_extraplayeritems) - Includes tons of utilities for 5+ games, such as 5+ player hud, extra kit / item spawning, and more * [l4d2_population_control](#l4d2_population_control) - Allows you to custom the type of zombies that spawn (% of clowns, mud men, etc..) * [globalbans](#globalbans) - Bans synced via mysql, way lighter than the sourcebans cesspool. - * [l4d2_rollback](#l4d2_rollback) - Abandoned but makes periodic backup of all player's items - * [l4d2_autorestart](#l4d2_autorestart) - Restarts server if it's been on for a certain uptime or when empty with just bots - * [l4d2_TKStopper](#l4d2_tkstopper) - All the teamkiller and shitty aim player punishments. Auto increasing reverse ff and teamkill detection - * [l4d2_crescendo_control](#l4d2_crescendo_control) - Prevents players from running far ahead and starting events & logs button presses + * [l4d2_rollback](#l4d2_rollback) - Abandoned and broken, but makes periodic backup of all player's items + * [l4d2_autorestart](#l4d2_autorestart) - Restarts server if it's been on for a certain uptime or when empty with just bots. + * [l4d2_TKStopper](#l4d2_tkstopper) - All the teamkiller and shitty-aim player punishments. Auto increasing reverse ff and teamkill detection + * [l4d2_crescendo_control](#l4d2_crescendo_control) - Prevents players from running far ahead and starting events, and logs button presses * [l4d2_vocalize_control](#l4d2_vocalize_control) - Allows you to locally mute someone from vocalizing - * [l4d2_guesswho](#l4d2_guesswho) - Garrys mod's guess who in l4d2, inspired by hide and seek + * [l4d2_guesswho](#l4d2_guesswho) - Garry's Mod's Guess Who in l4d2, inspired by hide and seek * [l4d2_hideandseek](#l4d2_hideandseek) - An enhancement to the base hide and seek mutation * [l4d2_hats](#l4d2_hats) - Entity Hats & Entity editing - * [l4d2_prophunt](#l4d2_prophunt) - Garry's mod inspired prop hunt, inspired by hide and seek + * [l4d2_prophunt](#l4d2_prophunt) - Garry's Mod inspired prop hunt, inspired by hide and seek * [sm_namespamblock](#sm_namespamblock) - Basic plugin that bans players if they change their name in rapid succession * [l4d2-stats-plugin](https://github.com/jackzmc/l4d2-stats-plugin) - Custom stats recorder, see https://stats.jackz.me + * [l4d2-ai-tweaks](#l4d2_ai_tweaks) - Very minor tweaks to survivor bots' behavior ### Modified Others * [200IQBots_FlyYouFools](#200iqbots_flyyoufools) - Improved code to make it support multiple tanks and work better * [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) - Use with [L4D2Tools](#l4d2tools) to change models, some fixes * [BetterWitchAvoidance](#betterwitchavoidance) -* l4d_anti_rush - Modified plugin to add a forward, so other plugins (like feedthetrolls) can do something and use highest flow value achieved for players (fixes issue when admins go back and players who haven't moved suddenly get punished) +* l4d_anti_rush - Modified plugin to add a forward, so other plugins (like feedthetrolls) can do something. In addition, use highest flow value achieved for players (fixes issue when admins go back and players who haven't moved suddenly get punished) * [l4d2_sb_fix](#l4d2_sb_fix) - Updated to 1.11 & latest sourcepawn syntax & removed the FCVAR_NOTIFY from all cvars (why is that added?) * GrabEnt - Improved version that prevents moving certain entities (such as invisual walls, ragdolls, etc) and improved some code @@ -55,7 +57,7 @@ Check the plugin info for an exact list. ### Development Dependencies Most L4D2 plugins use my own include: jutils.inc, it's provided in this repo. -Some do require newer includes by modified plugins (such as my improved survivor identity fix) +Some do require newer includes for my modified plugins (such as my improved survivor identity fix) ## Descriptions @@ -68,7 +70,7 @@ On knife kill, gives the player 100 HP (configurable) ### l4d2-manual-director -Probably going to be posted publicly sometime. allows you to spawn specials on cursor, or via director, forcefully, bypassing limits +~~Probably going to be posted publicly sometime.~~ Allows you to spawn specials on cursor, or via director, forcefully, bypassing limits * **Convars:** * `manual_director_version|mandirector_version` - ... gets version * `mandirector_notify_spawn <1/0>` - Should spawning specials notify on use? @@ -102,7 +104,7 @@ Technically 'l4d2 game info', haven't changed name. Just prints general informat ### AutoWarpBot -Simple l4d2 plugin that will auto teleport everyone to checkpoint once all real players have reached the saferoom. +Simple l4d2 plugin that will auto teleport bots to checkpoint once all real players have reached the saferoom. Doesn't really work well. Abandoned. @@ -119,7 +121,8 @@ Inspired by the 200IQBots_FlyYouFools. Bots avoid witch if its over 40% anger wh ### L4D2FFKickProtection Simple plugin that prevents a player that is being vote-kicked from doing any ff damage to teammates. -It also prevents vote kicking of admins, instead will kick the player and notify admins. +It also prevents vote kicking of admins, instead will notify admins. +It also makes any vote kicks created by an admin to instantly be accepted by all players * **Convars:** * `sm_votekick_force_threshold <#>` - The threshold of damage where the offending player is just immediately kicked. 0 -> Any attempted damage, -1 -> No auto kick. @@ -165,10 +168,10 @@ A collection of small tools: * Notification of when someone picks up laser sights (only the first user, includes bots), * Record time it takes for a finale or gauntlet run to be completed. * Record the amount of friendly fire damage done - * Set the survivor models of any survivor with updating [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) - * Automatically gives melee weapons that an idle bot dropped once no longer idle + * Set the survivor models of any survivor with updated [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) + * Automatically returns melee weapons that an idle bot dropped once no longer idle * Automatically make players go idle when ping spikes - * Slowly kill any zombies attacking survivor bot's blind spots (Fixes bots stuck taking damage and brain dead) + * Slowly kill any zombies attacking survivor bot's blind spots (Fixes brain dead bots stuck taking damage and not killing them) * **Convars:** * `sm_laser_use_notice <0/1>` - Enable notification of when a laser box was used first @@ -202,7 +205,7 @@ This really only affects wandering zombies, mobs and panic events, but it may wo Requires: * [Left4Dhooks](https://forums.alliedmods.net/showthread.php?t=321696) * (Optional) [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410) -* (Optional) [L4D2 Behavior](https://forums.alliedmods.net/showthread.php?p=2752139) - To be replaced with [Actions](https://forums.alliedmods.net/showthread.php?t=336374) +* (Optional) [Actions](https://forums.alliedmods.net/showthread.php?t=336374) * (Optional) [Modified L4D Antirush](#l4d_anti_rush) This plugin allows you to enact certain troll modes on any player, some are subtle some are less so. Either way, it works great to deal with a rusher, an asshole or even your friends. @@ -218,23 +221,25 @@ https://admin.jackz.me/docs/ftt * `sm_ftt_magnet_chance <0.0 - 1.0>` - % of the time that the magnet will work on a player." * `sm_ftt_shove_fail_chance <0.0 - 1.0>` - The % chance that a shove fails * **Commands:** - * `sm_fta [player]` - Opens a menu to select a troll to apply, with modifiers and flags + * `sm_fta [player]` - The main command, opens a menu to select a troll to apply, with modifiers and flags * `sm_ftr [player]` - Removes all active trolls from a player * `sm_ftc [player]` - Opens a menu to select a combo of trolls * `sm_ftl` - Lists all players that have a mode applied. * `sm_ftm` - Lists all troll options & their descriptions * `sm_mark` - Toggles marking a player to be banned when they fully disconnect - * `sm_insta [player] [special]` - (No arguments opens menu) - Spawns a special via director that will only target the victim - * `sm_inface [player] [special]` - Identical to above, but special will be spawned as close as possible to survivor. Boomers auto explode. - * `sm_bots_attack [target health]` - Slightly broken, but makes all bots shoot at player until they hit X health or a timeout is reached. + * `sm_insta [player] [special]` - (No arguments opens menu) - Spawns a special via the director that will only target the victim + * `sm_inface [player] [special]` - Identical to above, but special will be spawned as close as possible to survivor. Boomers auto explode, jockeys on their head, etc. + * `sm_bots_attack [target health]` - Slightly broken, but makes all bots shoot at player until they hit X health or a timeout is reached. Turn on `sb_friendlyfire` for it to be effective. * `sm_stagger ` - Makes a player stagger, shortcut to the Stagger troll * `sm_witch_attack ` - Makes all witches agro on the player - * `sm_scharge [timeout seconds]` - Will wait till there's no obstructions and players in the way and then spawns a charger to charge them. - * `sm_healbots [# bots or 0 default]` - Makes n amount of bots chase a player down to heal them. Won't stop until they are healed or die. + * `sm_scharge [timeout seconds]` - Will wait till there's no obstructions and players in the way, then spawns a charger behind them to charge them. + * `sm_healbots [# bots or 0 default]` - Makes n amount of bots chase a player down to heal them. Won't stop until they are healed, they die, or you run command again. ### l4d2_autobotcrown -Makes any suitable bot (> 40 hp, has shotgun) automatically crown a witch. Supports multiple bots and witches, but only one bot can crown one witch at a time. Plugin is obviously disabled in realism, and is really on suitable for coop or versus. Even works with idle players. +Makes any suitable bot (> 40 hp, has shotgun) automatically crown a witch. Supports multiple bots and witches, but only one bot can crown one witch at a time. Plugin is disabled in realism, and is really on suitable for coop or versus. Even works with idle players. + +Bots do sometimes miss, but sometimes still manage to kill witch. They also don't care if there is danger in the way (fire, acid, angry witch). * **Convars:** * `l4d2_autocrown_allowed_difficulty ` - The difficulties the plugin is active on. 1=Easy, 2=Normal 4=Advanced 8=Expert. Add numbers together. @@ -253,7 +258,8 @@ Features: * Automatically giving extra kits for each extra player in saferooms * Increasing item count for items randomly depending on player count * Fix same-models survivors having to fight over ammo pack usage -* Automatically lock the exit saferoom door until a threshold of players or time has passed +* Automatically lock the first saferoom door for every chapter, until a threshold of players or time has passed +* Includes a HUD that shows all the survivors and their items, and optionally their ping * **Convars:** * `l4d2_extraitem_chance` - The base chance (multiplied by player count) of an extra item being spawned. Default: 0.056 @@ -276,7 +282,7 @@ Requires: A fork of [Survivor Identity Fix plugin](https://forums.alliedmods.net/showthread.php?t=280539) that adds support for other plugins to update the model cache. This is used by [L4D2Tools](#L4D2Tools) to update the identity when someone changes their model with `sm_model`. It also will clear the memory of model when a player disconnects entirely or on a new map. -In addition, has a fix for the passing finale, and will automatically move L4D characters to L4D2 until finale starts preventing game messing up their characters. +In addition, has a fix for the passing finale, and will automatically temporarily change L4D characters to L4D2 until finale starts preventing game messing up their characters. ### l4d2_population_control @@ -328,7 +334,7 @@ Currently auto triggers: ### l4d2_autorestart Plugin that automatically restarts server when the server is NOT hibernating, with bots around and no players. -This fixes an issue with (shitty) custom maps that force sb_all_bot_game to 1 and disable hibernation +This fixes an issue with custom maps that force sb_all_bot_game to 1 and disable hibernation. ### l4d2_TKStopper Requires: @@ -345,7 +351,7 @@ Any survivor that attacks another survivor See https://admin.jackz.me/docs/plugins#tkstopper for some more implementation information -During any of the above three conditions, if they deal (or attempt to deal) over 75 HP in 15 seconds they will be instantly banned for a set period of time (60 minutes). If they are for sure a team killer, it can be extended to a permanent ban. +During any of the above three conditions, if they deal (or attempt to deal) over 75 HP in 15 seconds (configurable) they will be instantly banned for a set period of time (60 minutes). If they are for sure a team killer, it can be extended to a permanent ban. * **Cvars:** * `l4d2_tk_forgiveness_time <#>` - The minimum amount of seconds to pass (in seconds) where a player's previous accumulated FF is forgive. Default is 15s @@ -361,7 +367,7 @@ Requires: This plugin prevents the activation of buttons ahead of the team. It will prevent players from starting crescendos (and some small other activities as a side effect) until a certain threshold of the team has reached the area. -_This plugin is currently in **development.**_ Current implementation may be lacking. +_This plugin is not perfect, sometimes it may trigger early, or not trigger at all depending on the map. Sometimes you need to as admins, move forward to allow non-admins to activate events._ * **Cvars:** @@ -373,7 +379,7 @@ _This plugin is currently in **development.**_ Current implementation may be lac A very small plugin that simply allows a player to mute another player's vocalizations only for them. * **Commands:** - * `sm_vgag ` - Vocalize gag or ungags selected player(s) + * `sm_vgag ` - Vocalize gag or ungags selected player(s) for the command activator only ### l4d2_sb_fix A fork of https://forums.alliedmods.net/showthread.php?p=2757330 @@ -388,6 +394,7 @@ Requires: A sourcemod extenstion of the vscript gamemode (https://steamcommunity.com/sharedfiles/filedetails/?id=2467133506) - Player blockers, portals, and props to change and control the maps +- Optional climbable infected ladders, and heart beat when seeker nearby - Some quality of life (winner messages, change seeker mid game, change map time) - and a lot more @@ -403,7 +410,7 @@ Vscript required for hud & mutation Gamemode: https://steamcommunity.com/sharedfiles/filedetails/?id=2823719841 -Requires l4dtoolz and left4dhooks, and optioanlly skip intro cutscene +Requires l4dtoolz and left4dhooks, and optionally skip intro cutscene ### l4d2_prophunt Requires: @@ -418,7 +425,7 @@ Vscript required for hud & mutation * Demo Map: https://steamcommunity.com/sharedfiles/filedetails/?id=2855027013 (makes most prop_static -> prop_dynamic) -Requires l4dtoolz and left4dhooks, and optioanlly skip intro cutscene +Requires l4dtoolz and left4dhooks, and optionally skip intro cutscene ### l4d2_hats @@ -436,3 +443,11 @@ Requires recompile to change. * **Commands:** * `status2` - Shitty name, but shows all non-admin players, sorted by last joined ascending (up top). Shows steamid and the first name they joined the server as * `sm_status2` - Same command, but allows /status2 in chat + +### l4d2_ai_tweaks + +Simply, prevents an idle bot (that is a bot for an idle player) from healing another player unless: +1. The target is black and white +2. The player has been idle for over **ALLOW_HEALING_MIN_IDLE_TIME** (a \#define) seconds (default is 3 minutes) + +Requires recompile to change. diff --git a/scripting/activitymonitor.sp b/scripting/activitymonitor.sp index f44f618..01a9771 100644 --- a/scripting/activitymonitor.sp +++ b/scripting/activitymonitor.sp @@ -158,8 +158,9 @@ public Action Timer_PushLogs(Handle h) { static char query[1024]; static Log log; int length = logs.Length; - Transaction transaction = new Transaction(); + if(length > 0) { + Transaction transaction = new Transaction(); for(int i = 0; i < length; i++) { logs.GetArray(i, log, sizeof(log)); g_db.Format(query, sizeof(query), "INSERT INTO `activity_log` (`timestamp`, `server`, `type`, `client`, `target`, `message`) VALUES (%d, NULLIF('%s', ''), '%s', NULLIF('%s', ''), NULLIF('%s', ''), NULLIF('%s', ''))", @@ -173,11 +174,11 @@ public Action Timer_PushLogs(Handle h) { transaction.AddQuery(query); } logs.Resize(logs.Length - length); + g_db.Execute(transaction, _, SQL_TransactionFailed, length, DBPrio_Low); } - g_db.Execute(transaction, _, SQL_TransactionFailed, length, DBPrio_Low); return Plugin_Continue; } -public void SQL_TransactionFailed(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) { +void SQL_TransactionFailed(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData) { LogError("Push transaction failed: %s at query %d/%d", error, failIndex, numQueries); } @@ -301,4 +302,4 @@ public any Native_AddLog(Handle plugin, int numParams) { if(GetNativeString(4, message, sizeof(message)) != SP_ERROR_NONE) return false; AddLog(type, clientName, targetName, message); return true; -} \ No newline at end of file +} diff --git a/scripting/l4d2_TKStopper.sp b/scripting/l4d2_TKStopper.sp index 0f4a794..6aa3975 100644 --- a/scripting/l4d2_TKStopper.sp +++ b/scripting/l4d2_TKStopper.sp @@ -347,6 +347,8 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo float pos[3]; GetNearestPlayerPosition(victim, pos); PrintToConsoleAdmins("%N within join time (%d min), attempted to fall"); + TeleportEntity(victim, pos, NULL_VECTOR, NULL_VECTOR); + damage = 0.0; if(pData[victim].jumpAttempts > hSuicideLimit.IntValue) { if(hSuicideAction.IntValue == 1) { LogMessage("[NOTICE] Kicking %N for suicide attempts", victim, hBanTime.IntValue); @@ -362,50 +364,52 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo pData[victim].isTroll = true; } } + return Plugin_Stop; } - if(attacker == victim) return Plugin_Continue; - // Forgive player based on threshold, resetting accumlated damage + // Forgive player teamkill based on threshold, resetting accumlated damage if(time - pData[attacker].lastFFTime > hForgivenessTime.IntValue) { pData[attacker].TKDamageBuffer = 0.0; } + pData[attacker].TKDamageBuffer += damage; pData[attacker].totalDamageFF += damage; pData[attacker].totalFFCount++; pData[attacker].ffCount++; // Auto reverse ff logic - // If not immune to RFF, damage is direct, _or admin shit_ - if(~pData[attacker].immunityFlags & Immune_RFF && isDamageDirect) { + if(isDamageDirect) { + // Decrease the RFF scale based on how long since their last FF and an amount float minutesSinceiLastFFTime = (time - pData[attacker].lastFFTime) / 60.0; pData[attacker].autoRFFScaleFactor -= minutesSinceiLastFFTime * hFFAutoScaleForgivenessAmount.FloatValue; + if(pData[attacker].autoRFFScaleFactor < 0.0) { + pData[attacker].autoRFFScaleFactor = 0.0; + } + // Decrement any accumlated ff 'counts' int ffCountMinutes = RoundFloat(minutesSinceiLastFFTime / 10.0); pData[attacker].ffCount -= (ffCountMinutes * 2); if(pData[attacker].ffCount < 0) { pData[attacker].ffCount = 0; } - // Decrement any forgiven ratio (computed on demand) - if(pData[attacker].autoRFFScaleFactor < 0.0) { - pData[attacker].autoRFFScaleFactor = 0.0; - } - // Then calculate a new reverse ff ratio + + // Calculate a new reverse ff ratio based on scale threshold + damage dealt + # of recent friendly fires events pData[attacker].autoRFFScaleFactor += hFFAutoScaleAmount.FloatValue * damage * (pData[attacker].ffCount*0.25); if(pData[attacker].isTroll) { pData[attacker].autoRFFScaleFactor *= 2; - } - - // Cap max damage only for non-trolls - if(!pData[attacker].isTroll && hFFAutoScaleMaxRatio.FloatValue > 0.0 && pData[attacker].autoRFFScaleFactor > hFFAutoScaleMaxRatio.FloatValue) { + } else if(hFFAutoScaleMaxRatio.FloatValue > 0.0 && pData[attacker].autoRFFScaleFactor > hFFAutoScaleMaxRatio.FloatValue) { + // Cap it to the max - if they aren't marked as troll pData[attacker].autoRFFScaleFactor = hFFAutoScaleMaxRatio.FloatValue; } + } + int prevFFTime = pData[attacker].lastFFTime; pData[attacker].lastFFTime = time; // Check for excessive friendly fire damage in short timespan // If not immune to TK, if over TK threshold, not when escaping, and direct damage - if(~pData[attacker].immunityFlags & Immune_TK + if(~pData[attacker].immunityFlags & Immune_TK && pData[attacker].TKDamageBuffer > hThreshold.IntValue && !isFinaleEnding && isDamageDirect @@ -441,9 +445,9 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo } // Modify damages based on criteria - if(pData[victim].jumpAttempts > 0 - || L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim) - || time - pData[attacker].joinTime <= hJoinTime.IntValue * 60 + if(pData[victim].jumpAttempts > 0 // If they've attempted suicide, we just incase, disable their ff damage + || L4D_IsInFirstCheckpoint(victim) || L4D_IsInLastCheckpoint(victim) // No FF damage in saferooms + || time - pData[attacker].joinTime <= hJoinTime.IntValue * 60 // No FF damage within first X minutes ) { /* If the amount of seconds since they joined is <= the minimum join time cvar (min) threshold @@ -456,14 +460,14 @@ public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, flo }else if(isFinaleEnding) { // Keep admins immune if escape vehicle out, or if victim is a bot if(isAdmin || IsFakeClient(victim)) return Plugin_Continue; + // We ignore FF for fire/molotovs, can be accidental / non-issue if(isDamageDirect) SDKHooks_TakeDamage(attacker, attacker, attacker, damage * 2.0); damage = 0.0; return Plugin_Changed; - }else if(isDamageDirect && pData[attacker].autoRFFScaleFactor > 0.3) { // Ignore fire and propane damage, mistakes can happen + }else if(isDamageDirect && pData[attacker].autoRFFScaleFactor > 0.18) { // 0.18 buffer to ignore fire and propane damage, mistakes can happen // Apply their reverse ff damage, and have victim take a decreasing amount if(pData[attacker].isTroll) return Plugin_Stop; - else if(pData[attacker].immunityFlags & Immune_RFF) return Plugin_Continue; SDKHooks_TakeDamage(attacker, attacker, attacker, pData[attacker].autoRFFScaleFactor * damage); if(pData[attacker].autoRFFScaleFactor > 1.0) diff --git a/scripting/l4d2_autorestart.sp b/scripting/l4d2_autorestart.sp index 078001b..cfdbc59 100644 --- a/scripting/l4d2_autorestart.sp +++ b/scripting/l4d2_autorestart.sp @@ -21,7 +21,7 @@ public Plugin myinfo = { url = "https://github.com/Jackzmc/sourcemod-plugins" }; -ConVar noHibernate; +ConVar cvar_hibernateWhenEmpty; public void OnPluginStart() { startupTime = GetTime(); @@ -31,7 +31,7 @@ public void OnPluginStart() { SetFailState("This plugin is for L4D/L4D2 only."); } - noHibernate = FindConVar("sv_hibernate_when_empty"); + cvar_hibernateWhenEmpty = FindConVar("sv_hibernate_when_empty"); RegAdminCmd("sm_request_restart", Command_RequestRestart, ADMFLAG_GENERIC); @@ -60,7 +60,7 @@ public Action Timer_Check(Handle h) { } else if(GetTime() - startupTime > MAX_TIME_ONLINE_SECONDS) { LogAction(0, -1, "Server has passed max online time threshold, will restart if remains empty"); pendingRestart = true; - noHibernate.BoolValue = true; + cvar_hibernateWhenEmpty.BoolValue = false; if(IsServerEmpty()) { if(++triesEmpty > 4) { LogAction(0, -1, "Server has passed max online time threshold and is empty after %d tries, restarting now", triesEmpty); @@ -78,7 +78,7 @@ public Action Timer_Check(Handle h) { public void OnConfigsExecuted() { // Reset no hibernate setting when level changes: if(pendingRestart) { - noHibernate.BoolValue = true; + cvar_hibernateWhenEmpty.BoolValue = false; } } diff --git a/scripting/l4d2_baseball.sp b/scripting/l4d2_baseball.sp deleted file mode 100644 index cc763f5..0000000 --- a/scripting/l4d2_baseball.sp +++ /dev/null @@ -1,171 +0,0 @@ -#pragma semicolon 1 -#pragma newdecls required - -#include -#include - -#define HIT_1 "weapons/golf_club/wpn_golf_club_melee_01.wav" -#define HIT_2 "weapons/golf_club/wpn_golf_club_melee_02.wav" - -ConVar sv_melee_force_projectile, sv_melee_radius_projectile, sv_melee_force_boost_projectile_up; -int g_iLaser, g_iGlow; - -public Plugin myinfo = -{ - name = "[L4D2] Baseball", - author = "BHaType", - description = "Melee weapons can now deflect projectile", - version = "0.0", - url = "" -} - -public void OnPluginStart() -{ - sv_melee_force_projectile = CreateConVar("sv_melee_force_projectile", "0.6"); - sv_melee_force_boost_projectile_up = CreateConVar("sv_melee_force_boost_projectile_up", "250.0"); - sv_melee_radius_projectile = CreateConVar("sv_melee_radius_projectile", "75.0"); - - AutoExecConfig(true, "l4d2_baseball"); - - HookEvent("weapon_fire", weapon_fire); - HookEvent("entity_shoved", entity_shoved); -} - -public void OnMapStart() -{ - PrecacheSound(HIT_1, true); - PrecacheSound(HIT_2, true); - - g_iLaser = PrecacheModel("materials/sprites/laserbeam.vmt"); - g_iGlow = PrecacheModel("materials/sprites/glow.vmt"); -} - -public void entity_shoved (Event event, const char[] name, bool dontbroadcast) -{ - int entity = event.GetInt("entityid"); - - static char szName[36]; - GetEntityClassname(entity, szName, sizeof szName); - - if ( StrContains(szName, "_projectile") != -1 ) - { - float vVelocity[3]; - vVelocity = CalculateBaseForce(entity); - vVelocity[2] += sv_melee_force_boost_projectile_up.FloatValue; - - TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vVelocity); - } -} - -public void weapon_fire (Event event, const char[] name, bool dontbroadcast) -{ - static char szName[36]; - event.GetString("weapon", szName, sizeof szName); - - if ( strcmp(szName, "melee") != 0 ) - return; - - int client = event.GetInt("userid"); - timer (CreateTimer(0.1, timer, client, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE), client); -} - -public Action timer (Handle timer, int client) -{ - client = GetClientOfUserId(client); - - if ( !client ) - return Plugin_Stop; - - int weapon = GetPlayerWeaponSlot(client, 1); - - if ( weapon == -1 || GetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack") <= GetGameTime()) - return Plugin_Stop; - - float vAngles[3], vOrigin[3], vVector[3], vEnd[3]; - - GetClientEyePosition(client, vOrigin); - - GetClientEyeAngles(client, vAngles); - GetAngleVectors(vAngles, vVector, NULL_VECTOR, NULL_VECTOR); - ScaleVector(vVector, sv_melee_radius_projectile.FloatValue); - AddVectors(vOrigin, vVector, vEnd); - - GetClientEyePosition(client, vOrigin); - - #define hull 10.0 - static const float vMaxs[3] = { hull, hull, hull }; - static const float vMins[3] = { -hull, -hull, -hull }; - - TR_TraceHullFilter(vOrigin, vEnd, vMins, vMaxs, MASK_SOLID, TraceFilter, client); - float vHit[3]; - TR_GetEndPosition(vHit); - - if ( TR_DidHit () ) - { - int entity = TR_GetEntityIndex(); - - if ( entity != 0 ) - { - static char szName[36]; - GetEntityClassname(entity, szName, sizeof szName); - - if ( StrContains(szName, "_projectile") != -1 ) - { - float vVelocity[3], vVec[3]; - - vVelocity = CalculateBaseForce(entity, client); - GetEntPropVector(entity, Prop_Send, "m_vecOrigin", vOrigin); - - MakeVectorFromPoints(vHit, vOrigin, vVec); - ScaleVector(vVec, 1.0 - sv_melee_force_projectile.FloatValue * sv_melee_radius_projectile.FloatValue); - AddVectors(vVec, vVector, vVec); - AddVectors(vVec, vVelocity, vVelocity); - - TE_SetupSparks(vHit, vVelocity, 1, 1); - TE_SendToAll(); - - NegateVector(vVelocity); - - vVelocity[2] += sv_melee_force_boost_projectile_up.FloatValue; - TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vVelocity); - - int color[4] = { 255, ... }; - for (int i; i <= 2; i++) - color[i] = GetRandomInt(0, 255); - - TE_SetupBeamFollow(entity, g_iLaser, g_iGlow, 4.6, 0.8, 0.8, 1, color); - TE_SendToAll(); - - EmitSoundToAll((GetRandomInt(0, 1) == 0 ? HIT_1 : HIT_2), SOUND_FROM_WORLD, .origin = vHit); - - // PrintToChatAll("\x04%N \x03baseballed projectile for \x04%.2f \x03velocity!", client, GetVectorLength(vVelocity)); - return Plugin_Stop; - } - } - } - - return Plugin_Continue; -} - -float[] CalculateBaseForce (int victim, int attacker = 0) -{ - float m_vecBaseVelocity[3], m_vecVelocity[3], vAngles[3]; - - if ( attacker ) - { - GetEntPropVector(attacker, Prop_Send, "m_vecBaseVelocity", m_vecBaseVelocity); - GetClientEyeAngles(attacker, vAngles); - } - - GetEntPropVector(victim, Prop_Data, "m_vecVelocity", m_vecVelocity); - AddVectors(m_vecBaseVelocity, m_vecVelocity, m_vecVelocity); - - ScaleVector(m_vecVelocity, sv_melee_force_projectile.FloatValue); - - return m_vecVelocity; -} - -public bool TraceFilter (int entity, int mask, int data) -{ - return entity != data; -} \ No newline at end of file diff --git a/scripting/l4d2_hats.sp b/scripting/l4d2_hats.sp index f9db602..a21ca5e 100644 --- a/scripting/l4d2_hats.sp +++ b/scripting/l4d2_hats.sp @@ -61,8 +61,8 @@ public void OnPluginStart() { LoadTranslations("common.phrases"); HookEvent("player_entered_checkpoint", OnEnterSaferoom); HookEvent("player_left_checkpoint", OnLeaveSaferoom); - HookEvent("player_bot_replace", Event_PlayerOutOfIdle ); - HookEvent("bot_player_replace", Event_PlayerToIdle); + HookEvent("player_bot_replace", Event_PlayerToIdle); + HookEvent("bot_player_replace", Event_PlayerOutOfIdle); RegConsoleCmd("sm_hat", Command_DoAHat, "Hats"); RegAdminCmd("sm_hatf", Command_DoAHat, ADMFLAG_ROOT, "Hats"); @@ -470,28 +470,45 @@ void ChooseRandomPosition(float pos[3], int ignoreClient = 0) { public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) { float tick = GetGameTime(); + ////////////////////////////// + // OnPlayerRunCmd :: HATS + ///////////////////////////// if(IsHatsEnabled(client)) { int entity = GetHat(client); int visibleEntity = EntRefToEntIndex(hatData[client].visibleEntity); - ///#HAT PROCESS if(entity > 0) { - // try to tp hat to itys own pos + // Crash prevention: Prevent hat from touching ladder as that can cause server crashes if(!onLadder[client] && GetEntityMoveType(client) == MOVETYPE_LADDER) { onLadder[client] = true; ClearParent(entity); - // Hide hat temporarily in void: + + // If hat is not a player, we teleport them to the void (0, 0, 0) + // Otherwise, we just simply dismount the player while hatter is on ladder if(entity >= MaxClients) - TeleportEntity(entity, EMPTY_ANG, NULL_VECTOR, NULL_VECTOR); if(visibleEntity > 0) { hatData[client].visibleEntity = INVALID_ENT_REFERENCE; RemoveEntity(visibleEntity); } - } else if(onLadder[client] && GetEntityMoveType(client) != MOVETYPE_LADDER) { + } + // Player is no longer on ladder, restore hat: + else if(onLadder[client] && GetEntityMoveType(client) != MOVETYPE_LADDER) { onLadder[client] = false; EquipHat(client, entity); } + // Do the same crash protection for the hat itself, just to be safe: + if(entity <= MaxClients) { + if(!onLadder[entity] && GetEntityMoveType(entity) == MOVETYPE_LADDER) { + onLadder[entity] = true; + ClearParent(entity); + } else if(onLadder[entity] && GetEntityMoveType(entity) != MOVETYPE_LADDER) { + onLadder[entity] = false; + EquipHat(client, entity); + } + } + + // Rainbow hat processing if(HasFlag(client, HAT_RAINBOW)) { // Decrement and flip, possibly when rainbowticks if(hatData[client].rainbowReverse) { @@ -515,24 +532,18 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 EquipHat(client, entity); } - if(entity <= MaxClients) { - if(!onLadder[entity] && GetEntityMoveType(entity) == MOVETYPE_LADDER) { - onLadder[entity] = true; - ClearParent(entity); - } else if(onLadder[entity] && GetEntityMoveType(entity) != MOVETYPE_LADDER) { - onLadder[entity] = false; - EquipHat(client, entity); - } - } + // If bot is commandable and reversed (player reverse-hat common/survivor), change position: if(HasFlag(client, HAT_COMMANDABLE | HAT_REVERSED) && tickcount % 200 == 0) { float pos[3]; ChooseRandomPosition(pos, client); L4D2_CommandABot(entity, client, BOT_CMD_MOVE, pos); } } + // Detect E + R to offset hat or place down if(buttons & IN_USE && buttons & IN_RELOAD) { if(entity > 0) { if(buttons & IN_ZOOM) { + // Offset controls: if(buttons & IN_JUMP) hatData[client].offset[2] += 1.0; if(buttons & IN_DUCK) hatData[client].offset[2] -= 1.0; if(buttons & IN_FORWARD) hatData[client].offset[0] += 1.0; @@ -542,7 +553,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 TeleportEntity(entity, hatData[client].offset, angles, vel); return Plugin_Handled; } else if(tick - cmdThrottle[client] > 0.25) { - if(buttons & IN_ATTACK) { + if(buttons & IN_ATTACK) { // doesn't work reliably for some reason ClientCommand(client, "sm_hat y"); } else if(buttons & IN_DUCK) { ClientCommand(client, "sm_hat p"); @@ -558,7 +569,9 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 } } - ///#WALL BUILDER PROCESS + ////////////////////////////// + // OnPlayerRunCmd :: ENTITY EDITOR + ///////////////////////////// if(WallBuilder[client].IsActive() && WallBuilder[client].CheckEntity(client)) { if(buttons & IN_USE && buttons & IN_RELOAD) { ClientCommand(client, "sm_wall done"); @@ -578,6 +591,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 if(buttons & IN_ATTACK) WallBuilder[client].CycleAxis(client, tick); else if(buttons & IN_ATTACK2) WallBuilder[client].CycleSnapAngle(client, tick); + // Rotation control: if(tick - cmdThrottle[client] > 0.20) { if(WallBuilder[client].axis == 0) { if(mouse[1] > 10) WallBuilder[client].angles[0] += WallBuilder[client].snapAngle; @@ -876,4 +890,4 @@ stock bool GetCursorLimited2(int client, float distance, float endPos[3], TraceE return true; } return false; -} \ No newline at end of file +}