diff --git a/plugins/L4D2Tools.smx b/plugins/L4D2Tools.smx index c5ad3e0..e300b3b 100644 Binary files a/plugins/L4D2Tools.smx and b/plugins/L4D2Tools.smx differ diff --git a/plugins/l4d2_TKStopper.smx b/plugins/l4d2_TKStopper.smx index 9d041fc..2f461ce 100644 Binary files a/plugins/l4d2_TKStopper.smx and b/plugins/l4d2_TKStopper.smx differ diff --git a/plugins/l4d2_crescendo_control.smx b/plugins/l4d2_crescendo_control.smx index 5d95670..f095ad9 100644 Binary files a/plugins/l4d2_crescendo_control.smx and b/plugins/l4d2_crescendo_control.smx differ diff --git a/plugins/l4d2_extraplayeritems.smx b/plugins/l4d2_extraplayeritems.smx index 06e4e1a..17a7454 100644 Binary files a/plugins/l4d2_extraplayeritems.smx and b/plugins/l4d2_extraplayeritems.smx differ diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx index 7b82d12..2c01d40 100644 Binary files a/plugins/l4d2_feedthetrolls.smx and b/plugins/l4d2_feedthetrolls.smx differ diff --git a/plugins/l4d2_spawn_props.smx b/plugins/l4d2_spawn_props.smx new file mode 100644 index 0000000..d907a0c Binary files /dev/null and b/plugins/l4d2_spawn_props.smx differ diff --git a/plugins/l4d2_tank_priority.smx b/plugins/l4d2_tank_priority.smx index 5b0c5e8..01b8974 100644 Binary files a/plugins/l4d2_tank_priority.smx and b/plugins/l4d2_tank_priority.smx differ diff --git a/plugins/l4d2_turret.smx b/plugins/l4d2_turret.smx index b0b2ed7..57af84f 100644 Binary files a/plugins/l4d2_turret.smx and b/plugins/l4d2_turret.smx differ diff --git a/plugins/l4d_survivor_identity_fix.smx b/plugins/l4d_survivor_identity_fix.smx index 38b476c..c6e92df 100644 Binary files a/plugins/l4d_survivor_identity_fix.smx and b/plugins/l4d_survivor_identity_fix.smx differ diff --git a/plugins/sm_player_notes.smx b/plugins/sm_player_notes.smx index a24b8f4..ec8098c 100644 Binary files a/plugins/sm_player_notes.smx and b/plugins/sm_player_notes.smx differ diff --git a/scripting/L4D2Tools.sp b/scripting/L4D2Tools.sp index 06abeba..798c73d 100644 --- a/scripting/L4D2Tools.sp +++ b/scripting/L4D2Tools.sp @@ -428,7 +428,7 @@ public Action Command_StopSound(int client, int args) { client, target_list, MAXPLAYERS, - COMMAND_FILTER_CONNECTED, + COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0) @@ -511,7 +511,7 @@ public Action Command_SetClientModel(int client, int args) { client, target_list, MAXPLAYERS, - COMMAND_FILTER_ALIVE, + COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0) @@ -616,7 +616,7 @@ public Action Cmd_SetSurvivor(int client, int args) { client, target_list, MAXPLAYERS, - COMMAND_FILTER_CONNECTED, + COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0) @@ -762,7 +762,7 @@ public void Frame_HideEntity(int entity) { //STUCK BOTS WITH ZOMBIES FIX public Action Event_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) { if(attacker > MaxClients) { - char name[16]; + static char name[16]; GetEdictClassname(attacker, name, sizeof(name)); if(!StrEqual(name, "infected", true)) { return Plugin_Continue; diff --git a/scripting/include/feedthetrolls/base.inc b/scripting/include/feedthetrolls/base.inc index 58ab623..87a7075 100644 --- a/scripting/include/feedthetrolls/base.inc +++ b/scripting/include/feedthetrolls/base.inc @@ -4,7 +4,7 @@ //Allow MAX_TROLLS to be defined elsewhere #if defined MAX_TROLLS #else - #define MAX_TROLLS 48 + #define MAX_TROLLS 49 #endif enum trollModifier { diff --git a/scripting/include/feedthetrolls/commands.inc b/scripting/include/feedthetrolls/commands.inc index a16da4a..3cc0e60 100644 --- a/scripting/include/feedthetrolls/commands.inc +++ b/scripting/include/feedthetrolls/commands.inc @@ -202,7 +202,7 @@ public Action Command_ResetUser(int client, int args) { client, target_list, MAXPLAYERS, - COMMAND_FILTER_CONNECTED, /* Only allow alive players */ + COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_IMMUNITY, /* Only allow alive players */ target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -241,7 +241,7 @@ public Action Command_ApplyUser(int client, int args) { client, target_list, 1, - COMMAND_FILTER_NO_MULTI, + COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -556,15 +556,15 @@ public Action Command_Stagger(int client, int args) { static char arg1[32]; GetCmdArg(1, arg1, sizeof(arg1)); - int target_list[1], target_count; + int target_list[MAXPLAYERS], target_count; static char target_name[MAX_TARGET_LENGTH]; bool tn_is_ml; if ((target_count = ProcessTargetString( arg1, client, target_list, - 1, - COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI, + MaxClients, + COMMAND_FILTER_ALIVE, target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -574,7 +574,9 @@ public Action Command_Stagger(int client, int args) { return Plugin_Handled; } - L4D_StaggerPlayer(target_list[0], target_list[0], NULL_VECTOR); + for(int i = 0; i < target_count; i++) { + L4D_StaggerPlayer(target_list[i], target_list[i], NULL_VECTOR); + } } else { ReplyToCommand(client, "syntax: sm_stagger "); } @@ -635,7 +637,7 @@ public Action Command_HealTarget(int client, int args) { client, target_list, 1, - COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI, + COMMAND_FILTER_ALIVE | COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0 diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index 0e959f4..0e14b41 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -360,8 +360,10 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] static int honkID; static int profanityID; + static int typooId; if(honkID == 0) honkID = GetTrollID("Honk / Meow / Woof"); if(profanityID == 0) profanityID = GetTrollID("No Profanity"); + if(typooId == 0) typooId = GetTrollID("Typoos"); if(Trolls[honkID].IsActive(client) && Trolls[honkID].activeFlagClients[client] & 1) { // Honk Processing @@ -523,6 +525,23 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] // Print original in console no matter what PrintToServer("%N: %s", client, sArgs); return Plugin_Handled; + } else if(Trolls[typooId].IsActive(client)) { + char strings[32][MAX_TYPOS_LENGTH]; + int words = ExplodeString(sArgs, " ", strings, 32, MAX_TYPOS_LENGTH); + // Replace all typos + static char typoReplacement[32]; + for(int i = 0; i < words; i++) { + if(TYPOS_DICT.GetString(strings[i], typoReplacement, sizeof(typoReplacement))) { + strcopy(strings[i], MAX_TYPOS_LENGTH, typoReplacement); + } + } + int length = MAX_TYPOS_LENGTH * words; + char[] message = new char[length]; + ImplodeStrings(strings, 32, " ", message, length); + + CPrintToChatAll("{blue}%N {default}: %s", client, message); + PrintToServer("%N: %s", client, sArgs); + return Plugin_Handled; } return Plugin_Continue; } diff --git a/scripting/include/feedthetrolls/misc.inc b/scripting/include/feedthetrolls/misc.inc index 701a556..c903676 100644 --- a/scripting/include/feedthetrolls/misc.inc +++ b/scripting/include/feedthetrolls/misc.inc @@ -79,11 +79,38 @@ stock bool IsPlayerIncapped(int client) { return GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1; } +#define MAX_TYPOS_LENGTH 16 +StringMap TYPOS_DICT; +void LoadTypos() { + TYPOS_DICT.Clear(); + char sPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sPath, sizeof(sPath), "data/ftt_typos.txt"); + + if(!FileExists(sPath)) { + PrintToServer("[FTT] Missing typos list: data/ftt_typos.txt"); + return; + } + File file = OpenFile(sPath, "r", false, NULL_STRING); + if(file == null) { + PrintToServer("[FTT] Cannot open: data/ftt_typos.txt"); + return; + } + + + char buffer[140], key[32]; + while(file.ReadLine(buffer, sizeof(buffer))) { + int index = SplitString(buffer, " ", key, sizeof(key)); + TYPOS_DICT.SetString(key, buffer[index]); + } + + file.Close(); +} #define MAX_PHRASES_PER_WORD 8 #define MAX_PHRASE_LENGTH 191 StringMap REPLACEMENT_PHRASES; ArrayList fullMessagePhraseList; + /* Example: exWord { diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc index 4b9578d..e7bc0fa 100644 --- a/scripting/include/feedthetrolls/trolls.inc +++ b/scripting/include/feedthetrolls/trolls.inc @@ -110,6 +110,7 @@ void SetupTrolls() { /// CATEGORY: Chat SetCategory("Chat"); + SetupTroll("Typoos", "", TrollMod_Constant); SetupTroll("iCantSpellNoMore", "Chat messages letter will randomly changed with wrong letters", TrollMod_Constant); index = SetupTroll("No Profanity", "Replaces some words with random phrases", TrollMod_Constant); Trolls[index].AddFlagPrompt(false); diff --git a/scripting/include/ftt.inc b/scripting/include/ftt.inc index d23f85b..89f0929 100644 --- a/scripting/include/ftt.inc +++ b/scripting/include/ftt.inc @@ -101,6 +101,7 @@ float fAntiRushFrequencyCounter[MAXPLAYERS+1]; #define MODEL_CAR "models/props_vehicles/cara_95sedan.mdl" + #include #include #include diff --git a/scripting/include/left4dhooks.inc b/scripting/include/left4dhooks.inc index af5dfa6..23d5140 100644 --- a/scripting/include/left4dhooks.inc +++ b/scripting/include/left4dhooks.inc @@ -58,16 +58,16 @@ -// Natives: 227 -// L4D1 = 29 [left4downtown] + 47 [l4d_direct] + 15 [l4d2addresses] + 43 [silvers - mine!] + 4 [anim] = 133 -// L4D2 = 60 [left4downtown] + 61 [l4d_direct] + 26 [l4d2addresses] + 80 [silvers - mine!] + 4 [anim] = 224 +// Natives: 246 (including 3 for L4D1 only) +// L4D1 = 31 [left4downtown] + 47 [l4d_direct] + 16 [l4d2addresses] + 51 [silvers - mine!] + 4 [anim] = 149 +// L4D2 = 61 [left4downtown] + 59 [l4d_direct] + 32 [l4d2addresses] + 87 [silvers - mine!] + 4 [anim] = 243 -// Forwards: 144 -// L4D1 = 109; -// L4D2 = 142; +// Forwards: 172 (including 2 for L4D1 only) +// L4D1 = 126; +// L4D2 = 170; -// Stocks: 157 (L4D1 = 107, L4D2 = 153) -// left4dhooks_silver 41 stocks (L4D1 = 34, L4D2 = 41) +// Stocks: 163 (L4D1 = 109, L4D2 = 159) +// left4dhooks_silver 43 stocks (L4D1 = 36, L4D2 = 47) // left4dhooks_stocks 83 stocks (L4D1 = 44, L4D2 = 79) // left4dhooks_lux_library 34 stocks (L4D1 = 30, L4D2 = 34) @@ -75,7 +75,7 @@ -public SharedPlugin __pl_l4dh = +public SharedPlugin __pl_l4dh = { name = "left4dhooks", file = "left4dhooks.smx", @@ -106,19 +106,24 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D_PlayMusic"); MarkNativeAsOptional("L4D_StopMusic"); MarkNativeAsOptional("L4D_Dissolve"); - MarkNativeAsOptional("L4D_EstimateFallingDamage"); MarkNativeAsOptional("L4D_OnITExpired"); + MarkNativeAsOptional("L4D_EstimateFallingDamage"); + MarkNativeAsOptional("L4D_GetEntityWorldSpaceCenter"); MarkNativeAsOptional("L4D_AngularVelocity"); MarkNativeAsOptional("L4D_GetRandomPZSpawnPosition"); MarkNativeAsOptional("L4D_GetNearestNavArea"); MarkNativeAsOptional("L4D_GetLastKnownArea"); MarkNativeAsOptional("L4D2_GetFurthestSurvivorFlow"); MarkNativeAsOptional("L4D_FindRandomSpot"); + MarkNativeAsOptional("L4D2_IsVisibleToPlayer"); MarkNativeAsOptional("L4D_HasAnySurvivorLeftSafeArea"); MarkNativeAsOptional("L4D_IsAnySurvivorInStartArea"); MarkNativeAsOptional("L4D_IsAnySurvivorInCheckpoint"); + MarkNativeAsOptional("L4D_AreAllSurvivorsInFinaleArea"); MarkNativeAsOptional("L4D_IsInFirstCheckpoint"); MarkNativeAsOptional("L4D_IsInLastCheckpoint"); + MarkNativeAsOptional("L4D_IsPositionInFirstCheckpoint"); + MarkNativeAsOptional("L4D_IsPositionInLastCheckpoint"); MarkNativeAsOptional("L4D_GetCheckpointFirst"); MarkNativeAsOptional("L4D_GetCheckpointLast"); MarkNativeAsOptional("L4D2_IsReachable"); @@ -135,6 +140,9 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D_GetEntityFromAddress"); MarkNativeAsOptional("L4D_ReadMemoryString"); MarkNativeAsOptional("L4D_GetServerOS"); + MarkNativeAsOptional("Left4DHooks_Version"); + MarkNativeAsOptional("L4D2_GetScriptScope"); + MarkNativeAsOptional("L4D2_GetVScriptEntity"); MarkNativeAsOptional("L4D2_ExecVScriptCode"); MarkNativeAsOptional("L4D2_GetVScriptOutput"); MarkNativeAsOptional("L4D2_SpitterPrj"); @@ -143,6 +151,7 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D_SetHumanSpec"); MarkNativeAsOptional("L4D_TakeOverBot"); MarkNativeAsOptional("L4D_CanBecomeGhost"); + MarkNativeAsOptional("L4D_SetBecomeGhostAt"); MarkNativeAsOptional("L4D2_AreWanderersAllowed"); MarkNativeAsOptional("L4D2_GetCurrentFinaleStage"); MarkNativeAsOptional("L4D2_ForceNextStage"); @@ -150,8 +159,13 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D_ForceSurvivalStart"); MarkNativeAsOptional("L4D2_ForceScavengeStart"); MarkNativeAsOptional("L4D2_IsTankInPlay"); + MarkNativeAsOptional("L4D2_GetDirectorScriptScope"); MarkNativeAsOptional("L4D2_GetScriptValueInt"); + MarkNativeAsOptional("L4D2_GetScriptValueFloat"); + // MarkNativeAsOptional("L4D2_GetScriptValueString"); MarkNativeAsOptional("L4D2_NavAreaTravelDistance"); + MarkNativeAsOptional("L4D2_NavAreaBuildPath"); + MarkNativeAsOptional("L4D2_CommandABot"); MarkNativeAsOptional("L4D2_GetSurvivorSetMod"); MarkNativeAsOptional("L4D2_GetSurvivorSetMap"); MarkNativeAsOptional("L4D2_HasConfigurableDifficultySetting"); @@ -194,6 +208,10 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D2_GetWitchCount"); MarkNativeAsOptional("L4D_GetCurrentChapter"); MarkNativeAsOptional("L4D_GetMaxChapters"); + MarkNativeAsOptional("L4D_GetNavArea_SpawnAttributes"); + MarkNativeAsOptional("L4D_SetNavArea_SpawnAttributes"); + MarkNativeAsOptional("L4D_GetNavArea_AttributeFlags"); + MarkNativeAsOptional("L4D_SetNavArea_AttributeFlags"); MarkNativeAsOptional("L4D_ScavengeBeginRoundSetupTime"); MarkNativeAsOptional("L4D_ResetMobTimer"); @@ -337,6 +355,7 @@ public void __pl_l4dh_SetNTVOptional() MarkNativeAsOptional("L4D_ReviveSurvivor"); MarkNativeAsOptional("L4D_GetHighestFlowSurvivor"); MarkNativeAsOptional("L4D_GetInfectedFlowDistance"); + MarkNativeAsOptional("L4D_CleanupPlayerState"); MarkNativeAsOptional("L4D_TakeOverZombieBot"); MarkNativeAsOptional("L4D_ReplaceWithBot"); MarkNativeAsOptional("L4D_CullZombie"); @@ -376,7 +395,7 @@ enum GAMEMODE_VERSUS = 2, GAMEMODE_SURVIVAL = 4, GAMEMODE_SCAVENGE = 8 -} +}; // For the "L4D_GetPointer" native enum PointerType @@ -390,8 +409,9 @@ enum PointerType POINTER_MELEEINFO = 7, // @g_MeleeWeaponInfoStore (L4D2 Only) POINTER_EVENTMANAGER = 8, // pScriptedEventManager (L4D2 Only) POINTER_SCAVENGEMODE = 9, // pScavengeMode (L4D2 Only) - POINTER_VERSUSMODE = 10 // pVersusMode -} + POINTER_VERSUSMODE = 10, // pVersusMode + POINTER_SCRIPTVM = 11 // @g_pScriptVM (L4D2 Only) +}; // Provided by "BHaType": // For the "L4D_State_Transition" native. @@ -408,7 +428,7 @@ enum STATE_WAITING_FOR_RESCUE, STATE_GHOST, STATE_INTRO_CAMERA -} +}; // From: https://developer.valvesoftware.com/wiki/L4D2_Director_Scripts // For the "L4D2_ChangeFinaleStage" and "L4D2_GetCurrentFinaleStage" natives and "L4D2_OnChangeFinaleStage" forward. @@ -432,14 +452,77 @@ enum FINALE_GAUNTLET_BOSS_INCOMING = 15, FINALE_GAUNTLET_BOSS = 16, FINALE_GAUNTLET_ESCAPE = 17 -} +}; +// Used as the "reason" by the "L4D_OnKnockedDown*" forwards. enum { KNOCKDOWN_HUNTER = 1, KNOCKDOWN_TANK = 2, KNOCKDOWN_CHARGER = 3 -} +}; + +// From: https://developer.valvesoftware.com/wiki/List_of_L4D_Series_Nav_Mesh_Attributes +// NavArea Base Attributes: +enum +{ + NAV_BASE_CROUCH = 1, + NAV_BASE_JUMP = 2, + NAV_BASE_PRECISE = 4, + NAV_BASE_NO_JUMP = 8, + NAV_BASE_STOP = 16, + NAV_BASE_RUN = 32, + NAV_BASE_WALK = 64, + NAV_BASE_AVOID = 128, + NAV_BASE_TRANSIENT = 256, + NAV_BASE_DONT_HIDE = 512, + NAV_BASE_STAND = 1024, + NAV_BASE_NO_HOSTAGES = 2048, + NAV_BASE_STAIRS = 4096, + NAV_BASE_NO_MERGE = 8192, + NAV_BASE_OBSTACLE_TOP = 16384, + NAV_BASE_CLIFF = 32768, + NAV_BASE_TANK_ONLY = 65536, + NAV_BASE_MOB_ONLY = 131072, + NAV_BASE_PLAYERCLIP = 262144, + NAV_BASE_BREAKABLEWALL = 524288, + NAV_BASE_FLOW_BLOCKED = 134217728, + NAV_BASE_OUTSIDE_WORLD = 268435456, + NAV_BASE_MOSTLY_FLAT = 536870912, + NAV_BASE_HAS_ELEVATOR = 1073741824, + NAV_BASE_NAV_BLOCKER = -2147483648 +}; + +// NavArea Spawn Attributes: +enum +{ + NAV_SPAWN_EMPTY = 2, + NAV_SPAWN_STOP_SCAN = 4, + NAV_SPAWN_BATTLESTATION = 32, + NAV_SPAWN_FINALE = 64, + NAV_SPAWN_PLAYER_START = 128, + NAV_SPAWN_BATTLEFIELD = 256, + NAV_SPAWN_IGNORE_VISIBILITY = 512, + NAV_SPAWN_NOT_CLEARABLE = 1024, + NAV_SPAWN_CHECKPOINT = 2048, + NAV_SPAWN_OBSCURED = 4096, + NAV_SPAWN_NO_MOBS = 8192, + NAV_SPAWN_THREAT = 16384, + NAV_SPAWN_RESCUE_VEHICLE = 32768, + NAV_SPAWN_RESCUE_CLOSET = 65536, + NAV_SPAWN_ESCAPE_ROUTE = 131072, + NAV_SPAWN_DESTROYED_DOOR = 262144, + NAV_SPAWN_NOTHREAT = 524288, + NAV_SPAWN_LYINGDOWN = 1048576, + NAV_SPAWN_COMPASS_NORTH = 16777216, + NAV_SPAWN_COMPASS_NORTHEAST = 33554432, + NAV_SPAWN_COMPASS_EAST = 67108864, + NAV_SPAWN_COMPASS_EASTSOUTH = 134217728, + NAV_SPAWN_COMPASS_SOUTH = 268435456, + NAV_SPAWN_COMPASS_SOUTHWEST = 536870912, + NAV_SPAWN_COMPASS_WEST = 1073741824, + NAV_SPAWN_COMPASS_WESTNORTH = -2147483648 +}; @@ -552,7 +635,7 @@ forward void L4D_OnSpawnSpecial_Post(int client, int zombieClass, const float ve * @remarks zombieClass: 1=Smoker, 2=Boomer, 3=Hunter, 4=Spitter, 5=Jockey, 6=Charger * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param client The client index who spawned. Can be 0 if spawning failed + * @param client The client index who spawned. Can be 0 if spawning failed or -1 if blocked * @param zombieClass Zombie class that will be spawned * @param vecPos Vector coordinate where special will be spawned * @param vecAng QAngle where special will be facing @@ -577,7 +660,7 @@ forward Action L4D_OnSpawnTank(const float vecPos[3], const float vecAng[3]); * @remarks Not invoked if z_spawn tank is used and it gives a ghosted/dead player tank * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param client The client index who spawned (can be 0 if blocked in pre hook) + * @param client The client index who spawned * @param vecPos Vector coordinate where tank is spawned * @param vecAng QAngle where tank will be facing * @@ -590,7 +673,7 @@ forward void L4D_OnSpawnTank_Post(int client, const float vecPos[3], const float * @remarks Not invoked if z_spawn tank is used and it gives a ghosted/dead player tank * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param client The client index who spawned (can be 0 if blocked in pre hook) + * @param client The client index who spawned (can be -1 if blocked in pre hook) * @param vecPos Vector coordinate where tank is spawned * @param vecAng QAngle where tank will be facing * @@ -627,7 +710,7 @@ forward void L4D_OnSpawnWitch_Post(int entity, const float vecPos[3], const floa * @brief Called when a Witch spawns * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param entity Entity index that spawned + * @param entity Entity index that spawned (can be -1 if blocked) * @param vecPos Vector coordinate where witch is spawned * @param vecAng QAngle where witch will be facing * @@ -661,6 +744,20 @@ forward Action L4D2_OnSpawnWitchBride(const float vecPos[3], const float vecAng[ // L4D2 only. forward void L4D2_OnSpawnWitchBride_Post(int entity, const float vecPos[3], const float vecAng[3]); +/** + * @brief Called whenever ZombieManager::SpawnWitchBride(Vector&,QAngle&) is invoked + * @brief Called when a Witch Bride spawns + * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity Entity index that spawned (can be -1 if blocked) + * @param vecPos Vector coordinate where witch is spawned + * @param vecAng QAngle where witch will be facing + * + * @noreturn + */ +// L4D2 only. +forward void L4D2_OnSpawnWitchBride_PostHandled(int entity, const float vecPos[3], const float vecAng[3]); + /** * @brief Called whenever CDirector::OnMobRushStart(void) is invoked * @remarks called on random hordes, mini and finale hordes, and boomer hordes, causes Zombies to attack @@ -956,6 +1053,50 @@ forward void L4D_OnFirstSurvivorLeftSafeArea_Post(int client); */ forward void L4D_OnFirstSurvivorLeftSafeArea_PostHandled(int client); +/** + * @brief Called whenever CDirector::OnForceSurvivorPositions is invoked + * @remarks Triggers after round start when Survivors are forced into starting positions, usually first map and finale escape start, does not trigger on all maps + * + * @noreturn + */ +forward void L4D_OnForceSurvivorPositions_Pre(); + +/** + * @brief Called whenever CDirector::OnForceSurvivorPositions is invoked + * @remarks Triggers after round start when Survivors are forced into starting positions, usually first map and finale escape start, does not trigger on all maps + * + * @noreturn + */ +forward void L4D_OnForceSurvivorPositions(); + +/** + * @brief Called whenever CDirector::OnReleaseSurvivorPositions is invoked + * @remarks Triggers after round start when Survivors are released from the starting positions, usually first map and finale escape start, does not trigger on all maps + * + * @noreturn + */ +forward void L4D_OnReleaseSurvivorPositions(); + +/** + * @brief Called whenever "SpeakResponseConceptFromEntityIO" is invoked + * @remarks Triggers on concept talking + * + * @param entity Entity doing the talking (not always client indexes, might be a func_orator) + * + * @noreturn + */ +forward void L4D_OnSpeakResponseConcept_Pre(int entity); + +/** + * @brief Called whenever "SpeakResponseConceptFromEntityIO" is invoked + * @remarks Triggers on concept talking + * + * @param entity Entity doing the talking (not always client indexes, might be a func_orator) + * + * @noreturn + */ +forward void L4D_OnSpeakResponseConcept_Post(int entity); + /** * @brief Called whenever CTerrorPlayer::GetCrouchTopSpeed() is invoked * @remarks Constantly called to get players max Crouch speed @@ -992,6 +1133,7 @@ forward Action L4D_OnGetWalkTopSpeed(int target, float &retVal); /** * @brief Called whenever CDirector::GetScriptValue(const char*, int) is invoked * @remarks A script value is map specific + * @warning This forward may not fire on some keys due to C inline feature. Use L4D2_OnGetScriptValueInt instead to avoid the issue. * * @param key the script's key name * @param retVal what to override the return value with @@ -1004,6 +1146,7 @@ forward Action L4D_OnGetScriptValueInt(const char[] key, int &retVal); /** * @brief Called whenever CDirector::GetScriptValue(const char*, float) is invoked * @remarks A script value is map specific + * @warning This forward may not fire on some keys due to C inline feature. Use L4D2_OnGetScriptValueFloat instead to avoid the issue. * * @param key the script's key name * @param retVal what to override the return value with @@ -1013,9 +1156,24 @@ forward Action L4D_OnGetScriptValueInt(const char[] key, int &retVal); // L4D2 only. forward Action L4D_OnGetScriptValueFloat(const char[] key, float &retVal); +/** + * @brief Called whenever CDirector::GetScriptValue(const char*, Vector) is invoked + * @remarks A script value is map specific + * @warning This forward may not fire on some keys due to C inline feature. Use L4D2_OnGetScriptValueVector instead to avoid the issue. + * + * @param key the script's key name + * @param retVal what to override the return value with + * + * @return Plugin_Handled to override return value, Plugin_Continue otherwise. + */ +// L4D2 only. +// Unused, unable to determine if the return value is modified, and potentially different detour setup for linux +// forward Action L4D_OnGetScriptValueVector(const char[] key, float retVal[3]); + /** * @brief Called whenever CDirector::GetScriptValue(const char*, const char*, char*, int) is invoked * @remarks A script value is map specific + * @warning This forward may not fire on some keys due to C inline feature. Use L4D2_OnGetScriptValueString instead to avoid the issue. * * @param key the script's key name * @param defaultVal default key return, usually empty @@ -1026,6 +1184,87 @@ forward Action L4D_OnGetScriptValueFloat(const char[] key, float &retVal); // L4D2 only. forward Action L4D_OnGetScriptValueString(const char[] key, const char[] defaultVal, char retVal[128]); +// These are used for the "L4D2_OnGetScriptValueVoid" forward, and interally for the other "L4D2_OnGetScriptValue*" forwards: +enum fieldtype_t +{ + FIELD_VOID = 0, // No type or value + FIELD_FLOAT = 1, // Any floating point value + FIELD_VECTOR = 3, // Any vector, QAngle, or AngularImpulse + FIELD_INTEGER = 5, // Any integer or enum + FIELD_BOOLEAN = 6, // boolean, implemented as an int, I may use this as a hint for compression + FIELD_CHARACTER = 8, // a byte + FIELD_CSTRING = 31, + FIELD_UNSIGNED = 38, + FIELD_QANGLE = 40 +}; + +enum struct VariantBuffer +{ + int m_int; + float m_float; + char m_string[256]; + float m_vector[3]; +} + +/** + * @brief Called whenever CSquirrelVM::GetValue() is invoked + * @remarks A script value is map specific + * @remarks This is called when the searching key is not present in the Script VM. + * @remarks "retVal" accepts only 2 formats --- int:12345 or float:123.4 + * + * @param key the script's key name + * @param type the data type being set in retVal + * @param retVal what to override the return value with + * @param hScope The script scope (table) this was called from + * Use L4D2_GetDirectorScriptScope() if you want to make sure this was called by CDirector + * + * @return Plugin_Handled to override return value, Plugin_Continue otherwise. + */ +// L4D2 only. +forward Action L4D2_OnGetScriptValueVoid(const char[] key, fieldtype_t &type, VariantBuffer retVal, int hScope); + +/** + * @brief Called whenever CSquirrelVM::GetValue() is invoked + * @remarks A script value is map specific + * + * @param key the script's key name + * @param retVal what to override the return value with + * @param hScope The script scope (table) this was called from + * Use L4D2_GetDirectorScriptScope() if you want to make sure this was called by CDirector + * + * @return Plugin_Handled to override return value, Plugin_Continue otherwise. + */ +// L4D2 only. +forward Action L4D2_OnGetScriptValueInt(const char[] key, int &retVal, int hScope); + +/** + * @brief Called whenever CSquirrelVM::GetValue() is invoked + * @remarks A script value is map specific + * + * @param key the script's key name + * @param retVal what to override the return value with + * @param hScope The script scope (table) this was called from + * Use L4D2_GetDirectorScriptScope() if you want to make sure this was called by CDirector + * + * @return Plugin_Handled to override return value, Plugin_Continue otherwise. + */ +// L4D2 only. +forward Action L4D2_OnGetScriptValueFloat(const char[] key, float &retVal, int hScope); + +/** + * @brief Called whenever CSquirrelVM::GetValue() is invoked + * @remarks A script value is map specific + * + * @param key the script's key name + * @param retVal what to override the return value with + * @param hScope The script scope (table) this was called from + * Use L4D2_GetDirectorScriptScope() if you want to make sure this was called by CDirector + * + * @return Plugin_Handled to override return value, Plugin_Continue otherwise. + */ +// L4D2 only. +forward Action L4D2_OnGetScriptValueVector(const char[] key, float retVal[3], int hScope); + /** * @brief Called whenever CTerrorGameRules::HasConfigurableDifficultySetting() is invoked * @remarks used to deny/allow difficulty changes in different game modes @@ -1279,7 +1518,7 @@ forward void L4D_OnTryOfferingTankBot_Post(int tank_index, bool enterStasis); * @remarks Is used for displaying the "X gets Tank" window and transferring Tank control * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param tank_index Client index of the tank + * @param tank_index Client index of the tank (can be -1 when blocked) * @param enterStasis Is the tank in stasis * * @noreturn @@ -1618,36 +1857,70 @@ forward void L4D2_OnEntityShoved_PostHandled(int client, int entity, int weapon, * @brief Called whenever CTerrorPlayer::OnStaggered(CBaseEntity *, Vector const *) is invoked * @remarks Source is always null for Charger impacts (Valve) * - * @param target the client that is about to get staggered - * @param source the client that is about to stagger the target + * @param client the client that is about to get staggered + * @param source the client that is about to stagger the client * * @return Plugin_Handled to block, Plugin_Continue otherwise */ -forward Action L4D2_OnStagger(int target, int source); +forward Action L4D2_OnStagger(int client, int source); /** * @brief Called whenever CTerrorPlayer::OnStaggered(CBaseEntity *, Vector const *) is invoked * @remarks Source is always null for Charger impacts (Valve) * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param target the client that is about to get staggered - * @param source the client that is about to stagger the target + * @param client the client that is about to get staggered + * @param source the client that is about to stagger the client * * @noreturn */ -forward void L4D2_OnStagger_Post(int target, int source); +forward void L4D2_OnStagger_Post(int client, int source); /** * @brief Called whenever CTerrorPlayer::OnStaggered(CBaseEntity *, Vector const *) is invoked * @remarks Source is always null for Charger impacts (Valve) * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param target the client that is about to get staggered - * @param source the client that is about to stagger the target + * @param client the client that is about to get staggered + * @param source the client that is about to stagger the client * * @noreturn */ -forward void L4D2_OnStagger_PostHandled(int target, int source); +forward void L4D2_OnStagger_PostHandled(int client, int source); + +/** + * @brief Called when CTerrorPlayer::CancelStagger() is invoked. + * @remarks Called when a players staggering is about to be cancelled + * + * @param client Client index of the player. + * + * @return Plugin_Handled to allow staggering to continue, Plugin_Continue to allow. + **/ +forward Action L4D_OnCancelStagger(int client); + +/** + * @brief Called when CTerrorPlayer::CancelStagger() is invoked. + * @remarks Called when a players staggering is being cancelled + * @remarks This will not trigger if the cancel is being blocked by other plugins + * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param client Client index of the player. + * + * @return Plugin_Handled to allow staggering to continue, Plugin_Continue to allow. + **/ +forward void L4D_OnCancelStagger_Post(int client); + +/** + * @brief Called when CTerrorPlayer::CancelStagger() is invoked. + * @remarks Called when a players staggering has not been cancelled + * @remarks This will trigger if the cancel is being blocked by other plugins + * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param client Client index of the player. + * + * @return Plugin_Handled to allow staggering to continue, Plugin_Continue to allow. + **/ +forward void L4D_OnCancelStagger_PostHandled(int client); /** * @brief Called when CTerrorPlayer::Fling(Vector const&, PlayerAnimEvent_t, CBaseCombatCharacter*, float) is invoked. @@ -1680,7 +1953,7 @@ forward void L4D2_OnPlayerFling_Post(int client, int attacker, const float vecDi /** * @brief Called when CTerrorPlayer::Fling(Vector const&, PlayerAnimEvent_t, CBaseCombatCharacter*, float) is invoked. * @remarks Called when a player is flung to the ground. - * @remarks This will not trigger if the fling is being blocked by other plugins + * @remarks This will trigger if the fling is being blocked by other plugins * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * * @param client Client index of the player. @@ -1692,6 +1965,20 @@ forward void L4D2_OnPlayerFling_Post(int client, int attacker, const float vecDi // L4D2 only. forward void L4D2_OnPlayerFling_PostHandled(int client, int attacker, const float vecDir[3]); +/** + * @brief Called when CTerrorPlayer::IsMotionControlledXY is invoked. + * @remarks Called when a player is staggering or a Hunter has been knocked off someone. + * @remarks Blocking this will allow the player to fall with gravity instead of floating in the air. + * @remarks Pre-public release: various bugs surrounding this method. To be addressed later. + * + * @param client Client index of the player. + * @param activity The activity sequence playing that has overridden gravity + * + * @return Plugin_Handled to block, Plugin_Continue otherwise + **/ +#pragma deprecated This is for private testing and not released for public usage yet +forward Action L4D_OnMotionControlledXY(int client, int activity); + /** * @brief Called whenever CTerrorPlayer::OnShovedByPounceLanding(CTerrorPlayer*) is invoked * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled @@ -1728,8 +2015,8 @@ forward void L4D2_OnPounceOrLeapStumble_PostHandled(int victim, int attacker); /** * @brief Called when CTerrorPlayer::OnKnockedDown(CTerrorPlayer*) is invoked. * @remarks Called when someone is about to be hit by a Tank rock or lunged by a Hunter + * @remarks You can use the "KNOCKDOWN_*" enum values for the reason. * @remarks Called multiple times when someone is about to be has been pounced by a Hunter - * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * * @param victim Client index of the victim. * @param reason The Knockdown reason type. 1=Hunter lunge, 2=Tank rock, 3=Charger impact. @@ -1742,6 +2029,7 @@ forward Action L4D_OnKnockedDown(int client, int reason); * @brief Called when CTerrorPlayer::OnKnockedDown(CTerrorPlayer*) is invoked. * @remarks Called when someone is about to be hit by a Tank rock or lunged by a Hunter * @remarks Called multiple times when someone is about to be has been pounced by a Hunter + * @remarks You can use the "KNOCKDOWN_*" enum values for the reason. * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * * @param victim Client index of the victim. @@ -1755,6 +2043,7 @@ forward void L4D_OnKnockedDown_Post(int client, int reason); * @brief Called when CTerrorPlayer::OnKnockedDown(CTerrorPlayer*) is invoked. * @remarks Called when someone is about to be hit by a Tank rock or lunged by a Hunter * @remarks Called multiple times when someone is about to be has been pounced by a Hunter + * @remarks You can use the "KNOCKDOWN_*" enum values for the reason. * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * * @param victim Client index of the victim. @@ -1823,6 +2112,41 @@ forward Action L4D_OnFatalFalling(int client, int camera); **/ forward void L4D_OnFalling(int client); +/** + * @brief Called when CTerrorPlayer::Cough() is invoked. + * @remarks Called when a player is coughing, most likely from Smoker Cloud, but possibly called by other plugins. + * + * @param client Client index of the player affected. + * @param attacker Client index who caused the cough, can be 0 after a while when the AI special infected is kicked. + * + * @return return Plugin_Handled to block, Plugin_Continue otherwise. + **/ +forward Action L4D_OnPlayerCough(int client, int attacker); + +/** + * @brief Called when CTerrorPlayer::Cough() is invoked. + * @remarks Called when a player is coughing, most likely from Smoker Cloud, but possibly called by other plugins. + * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param client Client index of the player affected. + * @param attacker Client index who caused the cough, can be 0 after a while when the AI special infected is kicked. + * + * @noreturn + **/ +forward void L4D_OnPlayerCough_Post(int client, int attacker); + +/** + * @brief Called when CTerrorPlayer::Cough() is invoked. + * @remarks Called when a player is coughing, most likely from Smoker Cloud, but possibly called by other plugins. + * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param client Client index of the player affected. + * @param attacker Client index who caused the cough, can be 0 after a while when the AI special infected is kicked. + * + * @noreturn + **/ +forward void L4D_OnPlayerCough_PostHandled(int client, int attacker); + /** * @brief Called whenever CInferno::Spread(Vector const&) is invoked (only for spitters -- ignores fire) * @@ -1839,6 +2163,16 @@ forward void L4D_OnFalling(int client); // x,y,z has no affect, is 0,0,0 for spitter, molotovs is area size or something. forward Action L4D2_OnSpitSpread(int spitter, int projectile, float &x, float &y, float &z); +/** + * @brief Called whenever CTerrorPlayer::Extinguish() is invoked + * @remarks Called when a player (Survivor or Special Infected) is about to be extinguished + * + * @param client the client who is about to be extinguished + * + * @return Plugin_Handled to block extinguishing, Plugin_Continue otherwise + */ +forward Action L4D_PlayerExtinguish(int client); + /** * @brief Called when SurvivorBot::UseHealingItems(Action *) is invoked * @remarks Causes bots to use or give healing items (except in safe room on non-expert) @@ -1907,16 +2241,6 @@ forward void L4D_OnPouncedOnSurvivor_Post(int victim, int attacker); */ forward void L4D_OnPouncedOnSurvivor_PostHandled(int victim, int attacker); -/** - * @brief Called whenever CTerrorPlayer::Extinguish() is invoked - * @remarks Called when a player (Survivor or Special Infected) is about to be extinguished - * - * @param client the client who is about to be extinguished - * - * @return Plugin_Handled to block extinguishing, Plugin_Continue otherwise - */ -forward Action L4D_PlayerExtinguish(int client); - /** * @brief Called whenever CTerrorPlayer::GrabVictimWithTongue() is invoked * @remarks Called when a Survivor player is about to be grabbed by a Smoker @@ -1978,6 +2302,36 @@ forward Action L4D2_OnJockeyRide(int victim, int attacker); // L4D2 only. forward void L4D2_OnJockeyRide_Post(int victim, int attacker); +/** + * @brief Called whenever CTerrorPlayer::OnSlammedSurvivor() is invoked + * @remarks Called when a Survivor is slammed into a wall by a Charger, or on the first pummel if bWallSlam is 0 + * @bDeadlyCharge seems to always return 1 on Windows + * + * @param victim the client who's being slammed + * @param attacker the Charger slamming someone + * @param bWallSlam when slammed into a wall. Changing this can play a different animation + * @param bDeadlyCharge indicates the carry ends at a height down 360.0 units from the carry start, and adds DMG_PARALYZE to the damage flags to incap the victim. Changing this can incap the victim. + * + * @return Plugin_Changed to use overwritten values from plugin, Plugin_Continue otherwise + */ + // L4D2 only. +forward Action L4D2_OnSlammedSurvivor(int victim, int attacker, bool &bWallSlam, bool &bDeadlyCharge); + +/** + * @brief Called whenever CTerrorPlayer::OnSlammedSurvivor() is invoked + * @remarks Called when a Survivor is slammed into a wall by a Charger, or on the first pummel if bWallSlam is 0 + * @bDeadlyCharge seems to always return 1 on Windows + * + * @param victim the client who's being slammed + * @param attacker the Charger slamming someone + * @param bWallSlam when slammed into a wall. Changing this can play a different animation + * @param bDeadlyCharge indicates the carry ends at a height down 360.0 units from the carry start, and adds DMG_PARALYZE to the damage flags to incap the victim. Changing this can incap the victim. + * + * @noreturn + */ + // L4D2 only. +forward void L4D2_OnSlammedSurvivor_Post(int victim, int attacker, bool bWallSlam, bool bDeadlyCharge); + /** * @brief Called whenever CTerrorPlayer::OnStartCarryingVictim() is invoked * @remarks Called when a Survivor player is about to be carried by a Charger @@ -2127,7 +2481,7 @@ forward Action L4D_PipeBombProjectile_Pre(int client, float vecPos[3], float vec * @remarks Called when a PipeBomb projectile has been created * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param client the client who is throwing the grenade (can be 0) + * @param client the client who is throwing the grenade * @param projectile the projectile entity index (can be 0 if blocked in the pre hook) * @param vecPos the position vector of the projectile * @param vecAng the angle vector of the projectile @@ -2143,7 +2497,7 @@ forward void L4D_PipeBombProjectile_Post(int client, int projectile, const float * @remarks Called when a PipeBomb projectile has been created * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled * - * @param client the client who is throwing the grenade (can be 0) + * @param client the client who is throwing the grenade (can be -1 if blocked) * @param projectile the projectile entity index (can be 0 if blocked in the pre hook) * @param vecPos the position vector of the projectile * @param vecAng the angle vector of the projectile @@ -2154,13 +2508,121 @@ forward void L4D_PipeBombProjectile_Post(int client, int projectile, const float */ forward void L4D_PipeBombProjectile_PostHandled(int client, int projectile, const float vecPos[3], const float vecAng[3], const float vecVel[3], const float vecRot[3]); +/** + * @brief Called whenever CMolotovProjectile::Detonate is invoked + * @remarks Called when a Molotov projectile is about to explode + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @return Plugin_Handled to block explosion which will cause the Molotov projectile to bounce remain active, Plugin_Continue otherwise + */ +forward Action L4D_Molotov_Detonate(int entity, int client); + +/** + * @brief Called whenever CMolotovProjectile::Detonate is invoked + * @remarks Called when a Molotov projectile is about to explode + * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @noreturn + */ +forward void L4D_Molotov_Detonate_Post(int entity, int client); + +/** + * @brief Called whenever CMolotovProjectile::Detonate is invoked + * @remarks Called when a Molotov projectile is about to explode + * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @noreturn + */ +forward void L4D_Molotov_Detonate_PostHandled(int entity, int client); + +/** + * @brief Called whenever CPipeBombProjectile::Detonate is invoked + * @remarks Called when a PipeBomb projectile is about to explode + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @return Plugin_Handled to block explosion which will cause the PipeBomb projectile to continue flashing and remain active, Plugin_Continue otherwise + */ +forward Action L4D_PipeBomb_Detonate(int entity, int client); + +/** + * @brief Called whenever CPipeBombProjectile::Detonate is invoked + * @remarks Called when a PipeBomb projectile is about to explode + * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @noreturn + */ +forward void L4D_PipeBomb_Detonate_Post(int entity, int client); + +/** + * @brief Called whenever CPipeBombProjectile::Detonate is invoked + * @remarks Called when a PipeBomb projectile is about to explode + * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @noreturn + */ +forward void L4D_PipeBomb_Detonate_PostHandled(int entity, int client); + +/** + * @brief Called whenever CVomitJarProjectile::Detonate is invoked + * @remarks Called when a VomitJar projectile is about to explode + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @return Plugin_Handled to block explosion which will cause the Vomitjar projectile to bounce and remain active, Plugin_Continue otherwise + */ +// L4D2 only. +forward Action L4D2_VomitJar_Detonate(int entity, int client); + +/** + * @brief Called whenever CVomitJarProjectile::Detonate is invoked + * @remarks Called when a VomitJar projectile is about to explode + * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @noreturn + */ +// L4D2 only. +forward void L4D2_VomitJar_Detonate_Post(int entity, int client); + +/** + * @brief Called whenever CVomitJarProjectile::Detonate is invoked + * @remarks Called when a VomitJar projectile is about to explode + * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param entity the entity that exploded + * @param client the client who threw the grenade, can be -1 + * + * @noreturn + */ +// L4D2 only. +forward void L4D2_VomitJar_Detonate_PostHandled(int entity, int client); + /** * @brief Called whenever CInsectSwarm::CanHarm() is invoked * @remarks Called when Spitter Acid is determining if a client or entity can be damaged * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * * @param acid the acid entity index causing the damage - * @param spitter the Spitter or client who created the acid (can be 0) + * @param spitter the Spitter or client who created the acid (can be 0 or -1) * @param entity the client or entity index being checked if it can be damaged * * @return Plugin_Handled to block allowing damage to an entity, Plugin_Continue otherwise @@ -2174,7 +2636,7 @@ forward Action L4D2_CInsectSwarm_CanHarm(int acid, int spitter, int entity); * @remarks This forward will not trigger if the relative pre-hook forward has been blocked with Plugin_Handled * * @param acid the acid entity index causing the damage - * @param spitter the Spitter or client who created the acid (can be 0) + * @param spitter the Spitter or client who created the acid (can be 0 or -1) * @param entity the client or entity index being checked if it can be damaged * * @noreturn @@ -2182,6 +2644,20 @@ forward Action L4D2_CInsectSwarm_CanHarm(int acid, int spitter, int entity); // L4D2 only. forward void L4D2_CInsectSwarm_CanHarm_Post(int acid, int spitter, int entity); +/** + * @brief Called whenever CInsectSwarm::CanHarm() is invoked + * @remarks Called when Spitter Acid has determining if a client or entity can be damaged + * @remarks This forward will ONLY trigger if the relative pre-hook forward has been blocked with Plugin_Handled + * + * @param acid the acid entity index causing the damage + * @param spitter the Spitter or client who created the acid (can be 0 or -1) + * @param entity the client or entity index being checked if it can be damaged + * + * @noreturn + */ + // L4D2 only. +forward void L4D2_CInsectSwarm_CanHarm_PostHandled(int acid, int spitter, int entity); + /** * @brief Called whenever CBreakableProp::Break() is invoked * @remarks Called when a physics prop is broken @@ -2199,6 +2675,8 @@ forward void L4D_CBreakableProp_Break(int prop, int entity); * @remarks Called when a gascan is broken * * @param gascan the gascan entity index + * @param inflictor the inflictor entity index + * @param attacker the attacker entity index * * @noreturn */ @@ -2360,7 +2838,7 @@ native int L4D_GetClientFromAddress(Address addy); native int L4D_GetEntityFromAddress(Address addy); /** - * @brief Returns an entity index from a memory address + * @brief Returns a string read from a memory address * * @param addy Address to check * @@ -2375,6 +2853,44 @@ native int L4D_ReadMemoryString(Address addy, char[] buffer, int maxlength); */ native int L4D_GetServerOS(); +/** + * @brief Returns the Left4DHooks version number. For example version "1.122" is 1122. + * @note This will only work from Left4DHooks version 1.122 or newer. + * + * @remarks Use to verify a newer version is running from a certain version number, where for example a new native or forward was added. + * @remarks Each version number will be greater than the previous. + * @remarks Example code: + * @remarks Put: MarkNativeAsOptional("Left4DHooks_Version") in your plugins "AskPluginLoad2" section. + * @remarks Then run this code to determine the version: + * @remarks if( GetFeatureStatus(FeatureType_Native, "Left4DHooks_Version") == FeatureStatus_Available && Left4DHooks_Version() >= 1122 ) + * + * @remarks Alternatively, to support all versions, it would be better to use the "left4dhooks_version" convar, retrieve as a float and check. + * @remarks For example: FindConVar("left4dhooks_version").FloatValue >= 1.122 + * + * @return Version number + */ +native int Left4DHooks_Version(); + +/** +* @brief Returns an entities script scope (HSCRIPT) +* +* @param entity The entity to retrieve it's script scope +* +* @return Script scope or 0 if none. +*/ +// L4D2 only. +native int L4D2_GetScriptScope(int entity); + +/** +* @brief Creates or uses an existing "logic_script" entity that is created by Left4DHooks. +* @remarks Do NOT use entity outputs as this entity is shared by other plugins. +* +* @return VScript "logic_script" entity index +* @error 0 on error. +*/ +// L4D2 only. +native int L4D2_GetVScriptEntity(); + /** * @brief Runs a specified VScript code. * @remarks Saves having to create an entity. The entity can remain alive and used again... @@ -2421,7 +2937,7 @@ native int L4D_GetGameModeType(); * * @noreturn */ -native int L4D_Deafen(int client); +native void L4D_Deafen(int client); /** * @brief Returns a Survivors current temporary health buffer HP. @@ -2440,7 +2956,7 @@ native float L4D_GetTempHealth(int client); * * @noreturn */ -native int L4D_SetTempHealth(int client, float health); +native void L4D_SetTempHealth(int client, float health); /** * @brief Returns the reserve ammo for a specific players weapon @@ -2520,10 +3036,20 @@ native void L4D_OnITExpired(int client); * * @param client Client id of the player * - * @noreturn + * @return Estimated falling damage value */ native float L4D_EstimateFallingDamage(int client); +/** + * @brief Returns the worldspace center of an entity. + * + * @param entity The entity to use. + * @param vecPos The entities worldspace center returned vector + * + * @noreturn + */ +native void L4D_GetEntityWorldSpaceCenter(int entity, float vecPos[3]); + /** * @brief Sets a physics entity angular velocity vector. * @remarks Spins an entity, for example used in "Throwable Melee Weapons" plugin by Silvers @@ -2534,7 +3060,7 @@ native float L4D_EstimateFallingDamage(int client); * * @noreturn */ -native int L4D_AngularVelocity(int entity, const float vecAng[3]); +native void L4D_AngularVelocity(int entity, const float vecAng[3]); /** * @brief Attempts to find a random valid position to spawn a Special Infected. @@ -2555,10 +3081,15 @@ native bool L4D_GetRandomPZSpawnPosition(int client, int zombieClass, int attemp * @remarks This is more reliable than L4D2Direct_GetTerrorNavArea. * * @param vecPos The vector array to use to retrieve the NavArea + * @param maxDist Furthest distance to the nearest nav area + * @param anyZ Any ground position? + * @param checkLOS Check line of sight + * @param checkGround Check if on ground? + * @param teamID Team ID to check for * * @return The NavArea value, or 0 on failure probably */ -native any L4D_GetNearestNavArea(const float vecPos[3]); +native any L4D_GetNearestNavArea(const float vecPos[3], float maxDist = 300.0, bool anyZ = false, bool checkLOS = false, bool checkGround = false, int teamID = 2); /** * @brief Returns the nav address of the last known area. @@ -2588,6 +3119,19 @@ native float L4D2_GetFurthestSurvivorFlow(); */ native void L4D_FindRandomSpot(int NavArea, float vecPos[3]); +/** + * @brief Checks if a player is visible to a specified position. Should be slightly faster than using TR_TraceRayFilterEx. + * + * @param client Client to check visibility from + * @param vecPos The vector position of the target location + * @param team The team of the client, can possibly pass 0 to 3 + * @param team_target Target point team, if it is 0, the client's angle will be considered + * @param NavArea NavArea of ​​the target, or 0 to automatically get with GetNearestNavArea + * + * @return True if visible, false otherwise + */ +native bool L4D2_IsVisibleToPlayer(int client, int team, int team_target, int NavArea, float vecPos[3]); + /** * @brief Returns true when any survivor has left the starting area and true in Versus when the saferoom door automatically opens. * @@ -2609,6 +3153,13 @@ native bool L4D_IsAnySurvivorInStartArea(); */ native bool L4D_IsAnySurvivorInCheckpoint(); +/** + * @brief Returns true when all survivors are in the finale area. + * + * @return True when in all Survivors are in the area. False otherwise. + */ +native bool L4D_AreAllSurvivorsInFinaleArea(); + /** * @brief Returns true when the specified Survivor or Special Infected is in the starting checkpoint area. * @@ -2627,11 +3178,28 @@ native bool L4D_IsInFirstCheckpoint(int client); */ native bool L4D_IsInLastCheckpoint(int client); +/** + * @brief Returns true when a given vector is within the starting checkpoint area. + * + * @param vecPos Vector position to check + * + * @return True if the position is within the starting checkpoint area. False otherwise. + */ +native bool L4D_IsPositionInFirstCheckpoint(float vecPos[3]); + +/** + * @brief Returns true when a given vector is within the ending checkpoint area. + * + * @param vecPos Vector position to check + * + * @return True if the position is within the ending checkpoint area. False otherwise. + */ +native bool L4D_IsPositionInLastCheckpoint(float vecPos[3]); + /** * @brief Returns the entity index of the first saferoom door, if available. * @remarks This works by checking the saferoom door position against the maps flow distance selecting the door with 2000.0 (DOOR_RANGE_TOLLERANCE define) from map start. * @remarks Does not include doors with the DOOR_FLAG_IGNORE_USE flag. This flag is set by the "Safe Door Spam" plugin by Silvers for 0.1 seconds when someone attempts to open the saferoom door. - * * @return -1 if none exists or entity index of saferoom door */ @@ -2641,7 +3209,6 @@ native int L4D_GetCheckpointFirst(); * @brief Returns the entity index of the last saferoom door, if available. * @remarks This works by checking the saferoom door position against the maps flow distance selecting the door with 2000.0 (DOOR_RANGE_TOLLERANCE define) from map end. * @remarks Does not include doors with the DOOR_FLAG_IGNORE_USE flag. This flag is set by the "Safe Door Spam" plugin by Silvers for 0.1 seconds when someone attempts to open the saferoom door. - * * @return -1 if none exists or entity index of saferoom door */ @@ -2673,6 +3240,44 @@ native bool L4D2_IsReachable(int client, const float vecPos[3]); // L4D2 only. native float L4D2_NavAreaTravelDistance(float startPos[3], float endPos[3], bool ignoreNavBlockers); +/** + * @brief Test two vector positions if they can be reached (only returns false if a location has no valid NavArea, out-of-bounds can be valid). + * @remarks Uses the "ShortestPathCost" system. + * + * @param nav_startPos The NavArea address start position (can use L4D_GetNearestNavArea) + * @param nav_endPos The NavArea address end position from (can use L4D_GetNearestNavArea) + * @param flMaxPathLength Maximum distance allowed between the two points + * @param teamID Which team to validate the path for + * @param ignoreNavBlockers Should nav blockers be ignored or not + * + * @return Returns true if a path exists, false if not or on script error. + */ +// L4D2 only. +native bool L4D2_NavAreaBuildPath(Address nav_startPos, Address nav_endPos, float flMaxPathLength, int teamID, bool ignoreNavBlockers); + +// Used by "L4D2_CommandABot" native: +enum BOT_CMD +{ + BOT_CMD_ATTACK = 0, // Force the bot to attack a specific target, even bypassing CTerrorPlayer::SetSenseFlags (DirectorScript.BOT_CANT_SEE). + BOT_CMD_MOVE = 1, // Force the bot to move to a specific location, which then they will do it unconditionally without performing any other AI behaviours controlled by themselves. + // This means that Survivor Bots and most player-controllable Special Infected won't attack anything when commanded, but Common Infected still automatically attack enemies if they close in enough. + BOT_CMD_RETREAT = 2, // Force the bot to retreat from a target entity. Only works when used on Survivor Bots, and if target is a Tank. + BOT_CMD_RESET = 3 // Removes the active bot command and lets the AI resume controlling the bot. +}; + +/** + * @brief Uses the VScript "CommandABot" function to command a bot to attack, move, retreat or reset previous command + * + * @param entity The bot or infected to command + * @param target The Special Infected to target (used for types "BOT_CMD_ATTACK" and "BOT_CMD_RETREAT") + * @param type Type of command (see the "BOT_CMD" enum) + * @param vecPos Move to this location (Used for type "BOT_CMD_MOVE") + * + * @return Returns false when unable to perform, true otherwise. + */ +// L4D2 only. +native bool L4D2_CommandABot(int entity, int target, BOT_CMD type, float vecPos[3] = NULL_VECTOR); + /** * @brief Returns if players can control infected. * @@ -2689,7 +3294,7 @@ native bool L4D_HasPlayerControlledZombies(); * * @noreturn */ -native int L4D_DetonateProjectile(int entity); +native void L4D_DetonateProjectile(int entity); /** * @brief Detonates an active grenade projectile @@ -2793,7 +3398,7 @@ native void L4D2_UseAdrenaline(int client, float fTime = 15.0, bool heal = true) * @remarks Resets players stats for kills etc. * @remarks To preserve stats please view the code in "[L4D1 & L4D2] SM Respawn Improved" plugin by "Dragokas": https://forums.alliedmods.net/showthread.php?t=323220 * - * @param client Client ID of the person to respawn + * @param client Client ID of the person to respawn * * @noreturn */ @@ -2811,7 +3416,7 @@ native bool L4D_SetHumanSpec(int bot, int client); /** * @brief To takeover a Survivor bot. First use "ChangeClientTeam" and change them to 0. Then call "L4D_SetHumanSpec" then call "L4D_TakeOverBot". - * + * * @param client Client ID of who should takeover * * @return True or false @@ -2827,6 +3432,24 @@ native bool L4D_TakeOverBot(int client); */ native bool L4D_CanBecomeGhost(int client); +/** + * @brief Set a dead Special Infected players time until they transition into ghost state. Can be used when the "ghost_spawn_time" event triggers. + * + * @param client Client ID to check + * + * @return True or false + */ +native void L4D_SetBecomeGhostAt(int client, float time); + +/** + * @brief Sets a client as idle, afk - away from keyboard. + * + * @param client Client ID to check + * + * @return True or false + */ +native bool L4D_GoAwayFromKeyboard(int client); + /** * @brief Returns if Wandering Witches are allowed. * @@ -2891,8 +3514,26 @@ native void L4D2_ForceScavengeStart(); // L4D2 only. native bool L4D2_IsTankInPlay(); +/** + * @brief Returns the directors script scope handle + * @remarks Scope level + * 0 = DirectorScript + * 1 = MapScript + * 2 = LocalScript + * 3 = ChallengeScript + * 4 = DirectorOptionsScope + * + * @Param level The scope level + * + * @return Value of directors script scope + */ +// L4D2 only. +native int L4D2_GetDirectorScriptScope(int level); + /** * @brief Returns the value of the specified Director Variable key. + * @remarks You should provide a valid default value to use as the native is likely to return that unless the key value has been modified by a mutation or director script. + * @remarks See the "left4dhooks_test.sp" plugin and search for "L4D2_GetScriptValueInt" to see a list of keys and their default values and related cvars. * * @param key Director variable key name to search for * @param value Default value to use when the variable is not found @@ -2902,6 +3543,22 @@ native bool L4D2_IsTankInPlay(); // L4D2 only. native int L4D2_GetScriptValueInt(const char[] key, int value); +/** + * @brief Returns the value of the specified Director Variable key. + * @remarks You should provide a valid default value to use as the native is likely to return that unless the key value has been modified by a mutation or director script. + * @remarks See the "left4dhooks_test.sp" plugin and search for "L4D2_GetScriptValueFloat" to see a list of keys and their default values and related cvars. + * + * @param key Director variable key name to search for + * @param value Default value to use when the variable is not found + * + * @return Value of the variable, or provided default value on failure + */ +// L4D2 only. +native float L4D2_GetScriptValueFloat(const char[] key, float value); + +// Crashes when the key has not been set +// native void L4D2_GetScriptValueString(const char[] key, const char[] value, char[] retValue, int maxlength); + /** * @brief Returns if there is a configurable difficulty setting. * @brief Returns true for Coop/Realism modes. @@ -2976,14 +3633,6 @@ native bool L4D_IsVersusMode(); -// // Only returns default value provided. -// native float L4D2_GetScriptValueFloat(const char[] key, float value); - -// Not implemented, request if really required. -// native void L4D2_GetScriptValueString(const char[] key, const char[] value, char[] retValue, int maxlength); - - - // ==================================================================================================== @@ -3049,6 +3698,7 @@ native bool L4D2_VScriptWrapper_IsDying(int client); * @return True on success (does not guarantee effect turned on), false on script error. */ // L4D2 only. +#pragma deprecated Use L4D2_UseAdrenaline native instead (kept here for speed comparison) native bool L4D2_VScriptWrapper_UseAdrenaline(int client, float time); /** @@ -3087,12 +3737,13 @@ native any L4D2_VScriptWrapper_GetSenseFlags(int bot); * @return Returns true if a path exists, false if not or on script error. */ // L4D2 only. +#pragma deprecated Use L4D2_NavAreaBuildPath native instead (kept here for speed comparison) native bool L4D2_VScriptWrapper_NavAreaBuildPath(const float startPos[3], const float endPos[3], float flMaxPathLength, bool checkLOS, bool checkGround, int teamID, bool ignoreNavBlockers); /** * @brief Compute distance between two areas. * - * @return -1 if can't reach 'endArea' from 'startArea'. + * @return -1 if can't reach 'endArea' from 'startArea'. */ // L4D2 only. // Added as a demonstration and test, SDKCall is available, use "L4D2_NavAreaTravelDistance" instead. @@ -3394,6 +4045,44 @@ native int L4D_GetCurrentChapter(); */ native int L4D_GetMaxChapters(); +/** + * @brief Returns the nav area attribute flags + * + *param pTerrorNavArea Pointer to a NavArea + * + * @return Attribute flags + */ +native int L4D_GetNavArea_AttributeFlags(Address pTerrorNavArea); + +/** + * @brief Sets the nav area attribute flags + * + *param pTerrorNavArea Pointer to a NavArea + *param flags Attribute flags to set + * + * @return Attribute flags + */ +native void L4D_SetNavArea_AttributeFlags(Address pTerrorNavArea, int flags); + +/** + * @brief Returns the terror nav area attribute flags + * + *param pTerrorNavArea Pointer to a TerrorNavArea + * + * @return Attribute flags + */ +native int L4D_GetNavArea_SpawnAttributes(Address pTerrorNavArea); + +/** + * @brief Sets the terror nav area attribute flags + * + *param pTerrorNavArea Pointer to a TerrorNavArea + *param flags Attribute flags to set + * + * @noreturn + */ +native void L4D_SetNavArea_SpawnAttributes(Address pTerrorNavArea, int flags); + /** * @brief Gets the campaign scores stored in the Versus Director * @remarks These are the actual values used for campaign scores--not proxies @@ -3661,6 +4350,7 @@ enum L4D2IntWeaponAttributes L4D2IWA_ClipSize, L4D2IWA_Bucket, L4D2IWA_Tier, // L4D2 only + L4D2IWA_DefaultSize, // Default weapon clip size MAX_SIZE_L4D2IntWeaponAttributes }; @@ -4032,7 +4722,7 @@ native float L4D2Direct_GetVSTankFlowPercent(int roundNumber); * @noreturn * @error Director or Versus Director address not found. */ -native int L4D2Direct_SetVSTankFlowPercent(int roundNumber, float flow); +native void L4D2Direct_SetVSTankFlowPercent(int roundNumber, float flow); /** * Is there going to be a tank spawned during the given round @@ -4081,7 +4771,7 @@ native float L4D2Direct_GetVSWitchFlowPercent(int roundNumber); * @noreturn * @error Director or Versus Director address not found. */ -native int L4D2Direct_SetVSWitchFlowPercent(int roundNumber, float flow); +native void L4D2Direct_SetVSWitchFlowPercent(int roundNumber, float flow); /** * Is there going to be a witch spawned during the given round @@ -4103,7 +4793,7 @@ native bool L4D2Direct_GetVSWitchToSpawnThisRound(int roundNumber); * @noreturn * @error Director or Versus Director address not found. */ -native int L4D2Direct_SetVSWitchToSpawnThisRound(int roundNumber, bool spawn); +native void L4D2Direct_SetVSWitchToSpawnThisRound(int roundNumber, bool spawn); /** * Get a reference to the VersusStart CountdownTimer @@ -4214,6 +4904,7 @@ native void L4D2Direct_SetTankTickets(int client, int tickets); * @return Shove penalty or -1 on error */ // L4D2 only. +#pragma deprecated Use this instead: GetEntProp(client, Prop_Send, "m_iShovePenalty"); native int L4D2Direct_GetShovePenalty(int client); /** @@ -4226,6 +4917,7 @@ native int L4D2Direct_GetShovePenalty(int client); * @noreturn */ // L4D2 only. +#pragma deprecated Use this instead: SetEntProp(client, Prop_Send, "m_iShovePenalty", penalty); native void L4D2Direct_SetShovePenalty(int client, int penalty); /** @@ -4797,6 +5489,16 @@ native void L4D_ReplaceWithBot(int client); */ native void L4D_CullZombie(int client); +/** + * @brief Resets a players state equivalent to when they die + * @remarks does stuff like removing any pounces, stops reviving, stops healing, resets hang lighting, resets heartbeat and other sounds. + * + * @param client Client ID to affect + * + * @noreturn + */ +native void L4D_CleanupPlayerState(int client); + /** * @brief Sets a players zombie class, special infected can be alive and change! * @remarks Valid values L4D1: 1-3. L4D2: 1-6 diff --git a/scripting/include/left4dhooks_silver.inc b/scripting/include/left4dhooks_silver.inc index 014c11b..8262e5d 100644 --- a/scripting/include/left4dhooks_silver.inc +++ b/scripting/include/left4dhooks_silver.inc @@ -245,6 +245,41 @@ stock int L4D_EntityParent(int entity) return GetEntPropEnt(entity, Prop_Data, "m_pParent"); } +/** + * @brief Checks if a player is using any mounted weapon (minigun or 50cal) + * + * @param client Client to check + * + * @return true if using a mounted weapon, false otherwise + */ +stock bool IsUsingMinigun(int client) +{ + return ((GetEntProp(client, Prop_Send, "m_usingMountedWeapon") > 0) || (GetEntProp(client, Prop_Send, L4D_IsEngineLeft4Dead2() ? "m_usingMountedGun" : "m_usingMinigun") > 0)); +} + +/** + * @brief Stops a client using a mounted weapon + * + * @param client Entity index to check + * + * @return entity index or -1 if none + */ +stock void StopUsingMinigun(int client) +{ + if( IsUsingMinigun(client) ) + { + int entity = GetEntPropEnt(client, Prop_Send, "m_hUseEntity"); + if( entity > 0 && entity < 2048 ) + { + SetEntPropEnt(entity, Prop_Send, "m_owner", -1); + } + + SetEntProp(client, Prop_Send, L4D_IsEngineLeft4Dead2() ? "m_usingMountedGun" : "m_usingMinigun", 0); + SetEntProp(client, Prop_Send, "m_usingMountedWeapon", 0); + SetEntPropEnt(client, Prop_Send, "m_hUseEntity", -1); + } +} + // ================================================== @@ -675,6 +710,75 @@ stock bool L4D_HasReachedSmoker(int client) +// ================================================== +// CHARGER STOCKS - Written by "Forgetest" +// ================================================== +/** + * @brief Internally used to get offset to the start of queued pummel field. + * + * @return Offset into CTerrorPlayer to the start of queued pummel props + */ +static int L4D2_OffsQueuedPummelInfo() +{ + static int m_hQueuedPummelVictim = -1; + if ( m_hQueuedPummelVictim == -1 ) + m_hQueuedPummelVictim = FindSendPropInfo("CTerrorPlayer", "m_pummelAttacker") + 4; + + return m_hQueuedPummelVictim; +} + +/** + * @brief Returns the timestamp when the queued pummel begins. + * + * @param client Client ID of the player to check + * + * @return timestamp or -1.0 if no queued pummel + */ +stock float L4D2_GetQueuedPummelStartTime(int client) +{ + return GetEntDataFloat(client, L4D2_OffsQueuedPummelInfo() + 4); +} + +/** + * @brief Returns if a Charger is in a queued pummel. + * + * @param client Client ID of the player to check + * + * @return true if in queued pummel, false otherwise + */ +stock bool L4D2_IsInQueuedPummel(int client) +{ + float flTimestamp = L4D2_GetQueuedPummelStartTime(client); + + return flTimestamp != -1.0 && flTimestamp > GetGameTime(); +} + +/** + * @brief Returns the victim when a Charger is in a queued pummel. + * + * @param client Client ID of the player to check + * + * @return client index or -1 if none + */ +stock int L4D2_GetQueuedPummelVictim(int client) +{ + return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo()); +} + +/** + * @brief Returns the attacker when a Survivor is in a queued pummel. + * + * @param client Client ID of the player to check + * + * @return client index or -1 if none + */ +stock int L4D2_GetQueuedPummelAttacker(int client) +{ + return GetEntDataEnt2(client, L4D2_OffsQueuedPummelInfo() + 8); +} + + + // ================================================== // LEDGE HANG STOCKS // ================================================== @@ -885,7 +989,7 @@ stock void L4D_StopReviveAction(int client) owner_save = owner; } - if( target != -1 ) + if( target > 0 && target <= MaxClients ) { SetEntPropEnt(target, Prop_Send, "m_useActionOwner", -1); SetEntPropEnt(target, Prop_Send, "m_useActionTarget", -1); diff --git a/scripting/include/left4dhooks_stocks.inc b/scripting/include/left4dhooks_stocks.inc index 6e7810c..cf3f7c1 100644 --- a/scripting/include/left4dhooks_stocks.inc +++ b/scripting/include/left4dhooks_stocks.inc @@ -4,26 +4,26 @@ * Syntax Update and merge into "Left 4 DHooks Direct" (C) 2022 "SilverShot" * ============================================================================= * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License, version 3.0, as + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 3.0, as * published by the Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along + * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * As a special exception, AlliedModders LLC gives you permission to link the * code of this program (as well as its derivative works) to "Half-Life 2," - * the "Source Engine," the "SourcePawn JIT," and any Game MODs that run on + * the "Source Engine," the "SourcePawn JIT," and any Game MODs that run on * software by the Valve Corporation. You must obey the GNU General Public - * License in all respects for all other code used. Additionally, - * AlliedModders LLC grants this exception to all derivative works. - * AlliedModders LLC defines further exceptions, found in LICENSE.txt - * (as of this writing, version JULY-31-2007), or + * License in all respects for all other code used. Additionally, + * AlliedModders LLC grants this exception to all derivative works. + * AlliedModders LLC defines further exceptions, found in LICENSE.txt + * (as of this writing, version JULY-31-2007), or * . */ diff --git a/scripting/l4d2_TKStopper.sp b/scripting/l4d2_TKStopper.sp index 72916e5..b86f2a3 100644 --- a/scripting/l4d2_TKStopper.sp +++ b/scripting/l4d2_TKStopper.sp @@ -150,35 +150,63 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ // Special Infected Events /////////////////////////////////////////////////////////////////////////////// public Action Event_ChargerCarry(Event event, const char[] name, bool dontBroadcast) { - int victim = GetClientOfUserId(event.GetInt("victim")); + int userid = event.GetInt("victim"); + int victim = GetClientOfUserId(userid); if(victim) { - pData[victim].underAttack = StrEqual(name, "charger_carry_start"); + if(StrEqual(name, "charger_carry_start")) { + pData[victim].underAttack = true; + } else { + CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid); + } } return Plugin_Continue; } public Action Event_HunterPounce(Event event, const char[] name, bool dontBroadcast) { - int victim = GetClientOfUserId(event.GetInt("victim")); + int userid = event.GetInt("victim"); + int victim = GetClientOfUserId(userid); if(victim) { - pData[victim].underAttack = StrEqual(name, "lunge_pounce"); + if(StrEqual(name, "lunge_pounce")) { + pData[victim].underAttack = true; + } else { + CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid); + } } return Plugin_Continue; } public Action Event_SmokerChoke(Event event, const char[] name, bool dontBroadcast) { - int victim = GetClientOfUserId(event.GetInt("victim")); + int userid = event.GetInt("victim"); + int victim = GetClientOfUserId(userid); if(victim) { - pData[victim].underAttack = StrEqual(name, "choke_start"); + if(StrEqual(name, "choke_start")) { + pData[victim].underAttack = true; + } else { + CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid); + } } return Plugin_Continue; } public Action Event_JockeyRide(Event event, const char[] name, bool dontBroadcast) { - int victim = GetClientOfUserId(event.GetInt("victim")); + int userid = event.GetInt("victim"); + int victim = GetClientOfUserId(userid); if(victim) { - pData[victim].underAttack = StrEqual(name, "jockey_ride"); + if(StrEqual(name, "jockey_ride")) { + pData[victim].underAttack = true; + } else { + CreateTimer(1.0, Timer_StopSpecialAttackImmunity, userid); + } } return Plugin_Continue; } + +Action Timer_StopSpecialAttackImmunity(Handle h, int userid) { + int client = GetClientOfUserId(userid); + if(client > 0) { + pData[client].underAttack = false; + } + return Plugin_Continue; +} /////////////////////////////////////////////////////////////////////////////// // IDLE /////////////////////////////////////////////////////////////////////////////// @@ -463,7 +491,7 @@ public Action Command_TKInfo(int client, int args) { client, target_list, 1, - COMMAND_FILTER_NO_MULTI, + COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -551,7 +579,7 @@ public Action Command_IgnorePlayer(int client, int args) { client, target_list, MaxClients, - COMMAND_FILTER_ALIVE, + COMMAND_FILTER_NO_IMMUNITY | COMMAND_FILTER_NO_BOTS, target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -581,7 +609,6 @@ public Action Command_IgnorePlayer(int client, int args) { if (flags & Immune_RFF) { if (pData[target].immunityFlags & Immune_RFF) { LogAction(client, target, "\"%L\" re-enabled auto reverse friendly-fire for \"%L\"", client, target); - ShowActivity2(client, "[FTT] ", "%N has re-enabled auto reverse friendly-fire for %N", client, target); } else { LogAction(client, target, "\"%L\" disabled auto reverse friendly-fire for \"%L\"", client, target); ShowActivity2(client, "[FTT] ", "%N has disabled auto reverse friendly-fire for %N", client, target); diff --git a/scripting/l4d2_crescendo_control.sp b/scripting/l4d2_crescendo_control.sp index b2d9734..001806e 100644 --- a/scripting/l4d2_crescendo_control.sp +++ b/scripting/l4d2_crescendo_control.sp @@ -35,7 +35,7 @@ public void OnPluginStart() SetFailState("This plugin is for L4D2 only."); } - hEnabled = CreateConVar("l4d2_crescendo_control", "1", "Should plugin be active?", FCVAR_NONE, true, 0.0, true, 1.0); + hEnabled = CreateConVar("l4d2_crescendo_control", "1", "Should plugin be active?\n 1 = Enabled normally\n2 = Admins with bypass allowed only", FCVAR_NONE, true, 0.0, true, 1.0); hPercent = CreateConVar("l4d2_crescendo_percent", "0.5", "The percent of players needed to be in range for crescendo to start", FCVAR_NONE); hRange = CreateConVar("l4d2_crescendo_range", "250.0", "How many units away something range brain no work", FCVAR_NONE); @@ -84,7 +84,7 @@ public Action Timer_GetFlows(Handle h) { } public Action Event_ButtonPress(const char[] output, int entity, int client, float delay) { - if(hEnabled.BoolValue && client > 0 && client <= MaxClients) { + if(hEnabled.IntValue > 0 && client > 0 && client <= MaxClients) { AdminId admin = GetUserAdmin(client); if(admin != INVALID_ADMIN_ID && admin.HasFlag(Admin_Custom1)) return Plugin_Continue; @@ -98,7 +98,7 @@ public Action Event_ButtonPress(const char[] output, int entity, int client, flo float activatorFlow = L4D2Direct_GetFlowDistance(client); PrintToConsoleAll("[CC] Button Press by %N", client); - if(!IsActivationAllowed(activatorFlow, 1500.0)) { + if(hEnabled.IntValue == 2 || !IsActivationAllowed(activatorFlow, 1500.0)) { ClientCommand(client, "play ui/menu_invalid.wav"); PrintToChat(client, "Please wait for players to catch up."); AcceptEntityInput(entity, "Lock"); @@ -131,7 +131,7 @@ public void Frame_ResetButton(int entity) { stock bool IsActivationAllowed(float flowmax, float threshold) { // Broken behavior, just short circuit true - if(flowmax == 0.0) return true; + if(flowmax <= 0.01) return true; int farSurvivors, totalSurvivors; float totalFlow; diff --git a/scripting/l4d2_extraplayeritems.sp b/scripting/l4d2_extraplayeritems.sp index e575796..c172418 100644 --- a/scripting/l4d2_extraplayeritems.sp +++ b/scripting/l4d2_extraplayeritems.sp @@ -208,7 +208,7 @@ public void OnPluginStart() { hAddExtraKits = CreateConVar("l4d2_extraitems_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits, 1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0); hUpdateMinPlayers = CreateConVar("l4d2_extraitems_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO, 1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0); hMinPlayersSaferoomDoor = CreateConVar("l4d2_extraitems_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0); - hSaferoomDoorWaitSeconds = CreateConVar("l4d2_extraitems_doorunlock_wait", "55", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0); + hSaferoomDoorWaitSeconds = CreateConVar("l4d2_extraitems_doorunlock_wait", "25", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0); hSaferoomDoorAutoOpen = CreateConVar("l4d2_extraitems_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0); hEPIHudState = CreateConVar("l4d2_extraitems_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0); hExtraFinaleTank = CreateConVar("l4d2_extraitems_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0); @@ -563,7 +563,8 @@ public Action Event_GameStart(Event event, const char[] name, bool dontBroadcast } public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBroadcast) { - int client = GetClientOfUserId(event.GetInt("userid")); + int userid = event.GetInt("userid"); + int client = GetClientOfUserId(userid); if(GetClientTeam(client) == 2) { CreateTimer(1.5, Timer_RemoveInvincibility, client); SDKHook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken); @@ -604,7 +605,7 @@ public Action Event_PlayerFirstSpawn(Event event, const char[] name, bool dontBr } // If 5 survivors, then set them up, TP them. if(newCount > 4) { - RequestFrame(Frame_SetupNewClient, client); + CreateTimer(0.1, Timer_SetupNewClient, userid); } } } @@ -751,7 +752,9 @@ char TIER2_WEAPONS[9][] = { "weapon_shotgun_spas" }; -public void Frame_SetupNewClient(int client) { +public Action Timer_SetupNewClient(Handle h, int userid) { + int client = GetClientOfUserId(userid); + if(client == 0) return Plugin_Handled; if(!DoesClientHaveKit(client)) { int item = GivePlayerItem(client, "weapon_first_aid_kit"); EquipPlayerWeapon(client, item); @@ -798,12 +801,32 @@ public void Frame_SetupNewClient(int client) { TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR); } delete tier2Weapons; + float pos[3]; if(L4D2_IsValidWeapon(weaponName)) { - CheatCommand(client, "give", weaponName[7], ""); + int wpn = CreateEntityByName(weaponName); + DispatchSpawn(wpn); + SetEntProp(wpn, Prop_Send, "m_iClip1", L4D2_GetIntWeaponAttribute(weaponName, L4D2IWA_ClipSize)); + L4D_SetReserveAmmo(client, wpn, L4D2_GetIntWeaponAttribute(weaponName, L4D2IWA_Bullets)); + GetClientAbsOrigin(client, pos); + TeleportEntity(wpn, pos, NULL_VECTOR, NULL_VECTOR); + DataPack pack; + CreateDataTimer(0.2, Timer_GiveWeapon, pack); + pack.WriteCell(userid); + pack.WriteCell(wpn); } else { LogError("EPI: INVALID WEAPON: %s for %N", weaponName, client); } + return Plugin_Handled; +} +public Action Timer_GiveWeapon(Handle h, DataPack pack) { + pack.Reset(); + int userid = pack.ReadCell(); + int wpn = pack.ReadCell(); + int client = GetClientOfUserId(userid); + if(client > 0) { + EquipPlayerWeapon(client, wpn); + } } public Action Timer_RemoveInvincibility(Handle h, int client) { SDKUnhook(client, SDKHook_OnTakeDamage, OnInvincibleDamageTaken); @@ -1139,7 +1162,7 @@ void UnlockDoor(int entity, int flag) { if(IsValidEntity(entity)) { SetEntProp(entity, Prop_Send, "m_bLocked", 0); SDKUnhook(entity, SDKHook_Use, Hook_Use); - if(hSaferoomDoorAutoOpen.IntValue % flag == flag) { + if(hSaferoomDoorAutoOpen.IntValue & flag) { AcceptEntityInput(entity, "Open"); } firstSaferoomDoorEntity = -1; diff --git a/scripting/l4d2_feedthetrolls.sp b/scripting/l4d2_feedthetrolls.sp index 8024020..95508d8 100644 --- a/scripting/l4d2_feedthetrolls.sp +++ b/scripting/l4d2_feedthetrolls.sp @@ -49,7 +49,9 @@ public void OnPluginStart() { // Load core things (trolls & phrases): REPLACEMENT_PHRASES = new StringMap(); + TYPOS_DICT = new StringMap(); LoadPhrases(); + LoadTypos(); SetupTrolls(); SetupsTrollCombos(); diff --git a/scripting/l4d2_tank_priority.sp b/scripting/l4d2_tank_priority.sp index ae082e2..61fa273 100644 --- a/scripting/l4d2_tank_priority.sp +++ b/scripting/l4d2_tank_priority.sp @@ -16,7 +16,7 @@ public Plugin myinfo = author = "jackzmc", description = "", version = PLUGIN_VERSION, - url = "https://github.com/Jackzmc/sourcemod-plugins" + url = "https://gi thub.com/Jackzmc/sourcemod-plugins" }; #define TANK_CLASS_ID 8 @@ -83,6 +83,7 @@ public Action L4D2_OnChooseVictim(int attacker, int &curTarget) { int index = clients.Push(i); clients.Set(index, GetVectorDistance(clientPos, tankPos, true), 1); clients.Set(index, attacker, 2); + clients.Set(index, tankFlow - flow, 3); } } } @@ -116,12 +117,14 @@ int Sort_TankTargetter(int index1, int index2, Handle array, Handle hndl) { float distance1 = GetArrayCell(array, index1, 1); float distance2 = GetArrayCell(array, index2, 1); int tankIndex = GetArrayCell(array, index2, 2); + float flowDiff1 = GetArrayCell(array, index1, 3); + float flowDiff2 = GetArrayCell(array, index2, 3); /*500 units away, 0 damage vs 600 units away, 0 damage -> target closest 500 500 units away, 10 damage, vs 600 units away 0 damage 500 - 10 = 450 vs 600 */ - return (totalTankDamage[tankIndex][client1] + RoundFloat(distance1)) - (totalTankDamage[tankIndex][client2] + RoundFloat(distance2)); + return (totalTankDamage[tankIndex][client1] + RoundFloat(flowDiff1)) - (totalTankDamage[tankIndex][client2] + RoundFloat(flowDiff2)); } public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) { diff --git a/scripting/l4d2_turret.sp b/scripting/l4d2_turret.sp index 652ad9a..c6e5caa 100644 --- a/scripting/l4d2_turret.sp +++ b/scripting/l4d2_turret.sp @@ -12,6 +12,11 @@ #define TURRET_MAX_RANGE_SPECIALS_OPTIMIZED TURRET_MAX_RANGE_SPECIALS * TURRET_MAX_RANGE_SPECIALS #define TURRET_MAX_RANGE_INFECTED_OPTIMIZED TURRET_MAX_RANGE_INFECTED * TURRET_MAX_RANGE_INFECTED +#define TURRET_NORMAL_PHASE_TICKS 15 // The number of ticks to be in normal operation +#define TURRET_COMMON_PHASE_TICKS 5 // The number of ticks to clear out commons exclusively + +#define _TURRET_PHASE_TICKS TURRET_NORMAL_PHASE_TICKS + TURRET_COMMON_PHASE_TICKS + #define PLUGIN_VERSION "1.0" #include @@ -45,6 +50,8 @@ static int COLOR_RED_LIGHT[4] = { 150, 0, 0, 150 }; int manualTarget = -1; #define MANUAL_TARGETNAME "turret_target_manual" +ArrayList turretIds; + /* TODO: Entity_ChangeOverTime` @@ -84,16 +91,19 @@ public void OnPluginStart() { SetFailState("This plugin is for L4D/L4D2 only."); } + turretIds = new ArrayList(); + FindTurrets(); HookEvent("player_death", Event_PlayerDeath); + HookEvent("tank_killed", Event_PlayerDeath); cv_autoBaseDamage = CreateConVar("turret_auto_damage", "50.0", "The base damage the automatic turret deals", FCVAR_NONE, true, 0.0); cv_manualBaseDamage = CreateConVar("turret_manual_damage", "70.0", "The base damage the manual turret deals", FCVAR_NONE, true, 0.0); RegAdminCmd("sm_turret", Command_SpawnTurret, ADMFLAG_CHEATS); RegAdminCmd("sm_rmturrets", Command_RemoveTurrets, ADMFLAG_CHEATS); - RegAdminCmd("sm_rmturret", Command_RemoveTurrets, ADMFLAG_CHEATS); + RegAdminCmd("sm_rmturret", Command_RemoveTurret, ADMFLAG_CHEATS); RegAdminCmd("sm_manturret", Command_ManualTarget, ADMFLAG_CHEATS); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { @@ -117,6 +127,7 @@ TurretState turretState[2048]; int turretActivatorParticle[2048]; int entityActiveTurret[2048]; // mapping the turret thats active on victim. [victim] = turret int turretActiveEntity[2048]; +int turretPhaseOffset[2048]; // slight of offset so they dont all enter the same phase at same time bool turretIsActiveLaser[2048]; bool pendingDeletion[2048]; float turretDamage[2048]; @@ -148,6 +159,9 @@ void SetupTurret(int turret, float time = 0.0) { PrintToServer("Created turret think timer"); thinkTimer = CreateTimer(0.1, Timer_Think, _, TIMER_REPEAT); } + // Clamp to 0 -> _TURRET_PHASE_TICKS - 1 + turretPhaseOffset[turret] = turretIds.Length % (_TURRET_PHASE_TICKS - 1); + turretIds.Push(turret); } Action Timer_ActivateTurret(Handle h, int turret) { turretState[turret] = Turret_Idle; @@ -164,6 +178,7 @@ void DeactivateTurret(int turret) { } int ClearTurrets(bool fullClear = true) { + turretIds.Clear(); int entity = INVALID_ENT_REFERENCE; int count; char targetname[32]; @@ -237,10 +252,11 @@ public void OnMapEnd() { public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); - int index = event.GetInt("entindex"); + int index = event.GetInt("entindex", 0); int turret = entityActiveTurret[client]; if(turret > 0) { pendingDeletion[client] = true; + turretActiveEntity[turret] = 0; DeactivateTurret(turret); } entityActiveTurret[index] = 0; @@ -262,7 +278,7 @@ public void OnEntityDestroyed(int entity) { public Action Command_SpawnTurret(int client, int args) { float pos[3]; GetClientEyePosition(client, pos); - pos[2] += 40.0; + // pos[2] += 10.0; int base = CreateParticleNamed(ENT_PORTAL_NAME, PARTICLE_ELMOS, pos, NULL_VECTOR); SetupTurret(base, TURRET_ACTIVATION_TIME); ReplyToCommand(client, "New turret (%d) will activate in %.0f seconds", base, TURRET_ACTIVATION_TIME); @@ -312,6 +328,16 @@ public Action Command_RemoveTurrets(int client, int args) { return Plugin_Handled; } +public Action Command_RemoveTurret(int client, int args) { + if(turretIds.Length == 0) { + ReplyToCommand(client, "No turrets to remove"); + } else { + int lastTurret = turretIds.Get(turretIds.Length - 1); + ReplyToCommand(client, "Removed last turret %d", lastTurret); + } + return Plugin_Handled; +} + public Action Timer_Think(Handle h) { if( manualTargetter > 0) return Plugin_Continue; // Probably better to just store from CreateParticle @@ -319,7 +345,7 @@ public Action Timer_Think(Handle h) { entity = INVALID_ENT_REFERENCE; // static char targetname[32]; static float pos[3]; - static int count, target; + static int count, target, tick; while ((entity = FindEntityByClassname(entity, "info_particle_system")) != INVALID_ENT_REFERENCE) { // GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); @@ -338,17 +364,25 @@ public Action Timer_Think(Handle h) { entityActiveTurret[target] = 0; } DeactivateTurret(entity); - turretState[entity] = Turret_Idle; } // Skip activation if a survivor is too close if(FindNearestClient(TEAM_SURVIVORS, pos, TURRET_MAX_RANGE_HUMANS_OPTIMIZED) > 0) { continue; } + bool inNormalPhase = ((tick + turretPhaseOffset[entity]) % _TURRET_PHASE_TICKS) <= TURRET_NORMAL_PHASE_TICKS; + + // Find a target, in this order: Tank Rock -> Specials -> Infected float damage = cv_autoBaseDamage.FloatValue; - target = FindNearestVisibleEntity("tank_rock", pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED, entity); - if(target > 0) damage = 1000.0; - if(target == -1) target = FindNearestVisibleClient(TEAM_SPECIALS, pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED); + target = -1; + if(inNormalPhase) { + target = FindNearestVisibleEntity("tank_rock", pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED, entity); + if(target > 0) { + CreateTimer(1.2, Timer_KillRock, EntIndexToEntRef(target)); + damage = 1000.0; + } + if(target == -1) target = FindNearestVisibleClient(TEAM_SPECIALS, pos, TURRET_MAX_RANGE_SPECIALS_OPTIMIZED); + } if(target == -1) target = FindNearestVisibleEntity("infected", pos, TURRET_MAX_RANGE_INFECTED_OPTIMIZED, entity); if(target > 0) { turretDamage[entity] = damage; @@ -366,11 +400,21 @@ public Action Timer_Think(Handle h) { } } } - - + if(++tick >= _TURRET_PHASE_TICKS) { + tick = 0; + } return Plugin_Continue; } + +public Action Timer_KillRock(Handle h, int ref) { + int rock = EntRefToEntIndex(ref); + if(rock != INVALID_ENT_REFERENCE) { + L4D_DetonateProjectile(rock); + } + return Plugin_Handled; +} + static float TURRET_LASER_COLOR[3] = { 0.0, 255.0, 255.0 }; void FireTurretAuto(const float origin[3], int targetEntity, float damage = 105.0) { @@ -486,12 +530,16 @@ stock int FindNearestVisibleClient(int team, const float origin[3], float maxRan int client = -1; float closestDist, pos[3]; for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == team && !pendingDeletion[i]) { + if(!pendingDeletion[i] && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == team) { GetClientAbsOrigin(i, pos); float distance = GetVectorDistance(origin, pos, true); if(maxRange > 0.0 && distance > maxRange) continue; - if(client == -1 || distance <= closestDist) { + if(distance <= closestDist || client == -1) { if(CanSeePoint(origin, pos)) { + // Priority: Pinned survivors + if(L4D_GetPinnedSurvivor(i)) { + return i; + } client = i; closestDist = distance; } @@ -520,7 +568,7 @@ stock int FindNearestVisibleEntity(const char[] classname, const float origin[3] } stock bool CanSeePoint(const float origin[3], const float point[3]) { - TR_TraceRay(origin, point, MASK_ALL, RayType_EndPoint); + TR_TraceRay(origin, point, MASK_SOLID, RayType_EndPoint); return !TR_DidHit(); // Can see point if no collisions } diff --git a/scripting/l4d_survivor_identity_fix.sp b/scripting/l4d_survivor_identity_fix.sp index 625ea82..dd7ba4e 100644 --- a/scripting/l4d_survivor_identity_fix.sp +++ b/scripting/l4d_survivor_identity_fix.sp @@ -314,9 +314,9 @@ public void Frame_CheckClient(int userid) { //A model is set: Fetched from cookie if(g_iPendingCookieModel[client]) { CreateTimer(0.2, Timer_SetClientModel, client); - } else { + }/* else { CreateTimer(0.2, Timer_SetAllCookieModels); - } + }*/ //FIXME: Possibly causing people to become rochelle weirdly }else{ //Model was not set: Use least-used survivor. @@ -365,11 +365,7 @@ public Action Event_PlayerDeath(Event event, const char[] name, bool dontBroadca } return Plugin_Continue; } -public void OnClientPutInServer(int client) { - if(GetClientTeam(client) == 2 && StrEqual(currentMap, "c6m3_port")) { - - } -} + //On finale start: Set back to their L4D1 character. public Action Event_FinaleStart(Event event, const char[] name, bool dontBroadcast) { diff --git a/scripting/sm_player_notes.sp b/scripting/sm_player_notes.sp index 15033e9..19e8c13 100644 --- a/scripting/sm_player_notes.sp +++ b/scripting/sm_player_notes.sp @@ -101,13 +101,17 @@ public Action OnClientSayCommand(int client, const char[] command, const char[] if(StrEqual(sArgs, "cancel", false)) { PrintToChat(client, "Note cancelled."); } else { + int size = strlen(sArgs); + char[] sArgsTrimmed = new char[size]; + strcopy(sArgsTrimmed, size, sArgs); + TrimString(sArgsTrimmed); char buffer[32]; GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer)); - DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgs); + DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgsTrimmed); DB.Query(DB_AddNote, query); - LogAction(client, -1, "\"%L\" added note for \"%s\": \"%s\"", client, menuNoteTarget, sArgs); + LogAction(client, -1, "\"%L\" added note for \"%s\": \"%s\"", client, menuNoteTarget, sArgsTrimmed); Format(buffer, sizeof(buffer), "%N: ", client); - CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTarget, sArgs); + CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTarget, sArgsTrimmed); } return Plugin_Stop; } @@ -121,6 +125,7 @@ public Action Command_AddNote(int client, int args) { char target_name[MAX_TARGET_LENGTH]; GetCmdArg(1, target_name, sizeof(target_name)); GetCmdArg(2, reason, sizeof(reason)); + TrimString(reason); int target_list[1], target_count; bool tn_is_ml; @@ -129,7 +134,7 @@ public Action Command_AddNote(int client, int args) { client, target_list, 1, - COMMAND_FILTER_NO_MULTI, + COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -170,7 +175,7 @@ public Action Command_ListNotes(int client, int args) { client, target_list, 1, - COMMAND_FILTER_NO_MULTI, + COMMAND_FILTER_NO_MULTI | COMMAND_FILTER_NO_IMMUNITY, target_name, sizeof(target_name), tn_is_ml)) <= 0 @@ -259,6 +264,7 @@ public void DB_FindNotes(Database db, DBResultSet results, const char[] error, a while(results.FetchRow()) { results.FetchString(0, reason, sizeof(reason)); results.FetchString(1, noteCreator, sizeof(noteCreator)); + TrimString(reason); if(ParseActions(data, reason)) { actions++; } else {