FlacoBey

Untitled

Jan 16th, 2019
148
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.78 KB | None | 0 0
  1. #pragma semicolon 1
  2.  
  3. #include <sourcemod>
  4. #include <sdkhooks>
  5.  
  6. #define PLUGIN_VERSION "1.3"
  7. #define PLUGIN_NAME "L4D2 Realism Penetration Fix"
  8. #define CVAR_FLAGS FCVAR_PLUGIN|FCVAR_NOTIFY
  9.  
  10. // %1 = client
  11. #define IS_VALID_CLIENT(%1) (%1 > 0 && %1 <= MaxClients)
  12. #define IS_CONNECTED_INGAME(%1) (IsClientConnected(%1) && IsClientInGame(%1))
  13. #define IS_SURVIVOR(%1) (GetClientTeam(%1) == 2)
  14. #define IS_INFECTED(%1) (GetClientTeam(%1) == 3)
  15.  
  16. #define IS_VALID_INGAME(%1) (IS_VALID_CLIENT(%1) && IS_CONNECTED_INGAME(%1))
  17.  
  18. #define IS_VALID_SURVIVOR(%1) (IS_VALID_INGAME(%1) && IS_SURVIVOR(%1))
  19. #define IS_VALID_INFECTED(%1) (IS_VALID_INGAME(%1) && IS_INFECTED(%1))
  20.  
  21. #define IS_SURVIVOR_ALIVE(%1) (IS_VALID_SURVIVOR(%1) && IsPlayerAlive(%1))
  22. #define IS_INFECTED_ALIVE(%1) (IS_VALID_INFECTED(%1) && IsPlayerAlive(%1))
  23.  
  24.  
  25. new g_iEnabled;
  26. new bool:g_bDebug;
  27. new g_iNerfMagnum;
  28. new g_iBuffSniper;
  29. new g_iMagnumPenetrationLimit;
  30. new g_iSniperPenetrationLimit;
  31.  
  32. new g_iCurrentPenetrationCount[MAXPLAYERS+1] = { 0, ... };
  33.  
  34. new Float:g_fDifficultyMultiplier;
  35. new bool:g_bRealism;
  36.  
  37. public Plugin:myinfo =
  38. {
  39.     name = PLUGIN_NAME,
  40.     author = "dcx2",
  41.     description = "Fixes penetration on Realism (or all) by buffing sniper and/or nerfing magnum",
  42.     version = PLUGIN_VERSION,
  43.     url = "www.AlliedMods.net"
  44. }
  45.  
  46. public OnPluginStart()
  47. {
  48.     Require_L4D2();
  49.    
  50.     new Handle:cvarEnable = CreateConVar("sm_realismpenfix_enable", "1.0", "Enables this plugin (1 = realism only, 2 = all game modes)", CVAR_FLAGS);
  51.     new Handle:cvarDebug = CreateConVar("sm_realismpenfix_debug", "0.0", "Print debug output.", CVAR_FLAGS);
  52.     new Handle:cvarNerfMagnum = CreateConVar("sm_realismpenfix_nerfmagnum", "1.0", "0: full penetration bonus\n1: half penetration bonus\n2: no penetration bonus, no distance penalty\n3: no penetration bonus, plus distance damage penalty", CVAR_FLAGS);
  53.     new Handle:cvarBuffSniper = CreateConVar("sm_realismpenfix_buffsniper", "1.0", "0: no penetration bonus\n1: half penetration bonus\n2: full penetration bonus", CVAR_FLAGS);
  54.     new Handle:cvarMagnumLimit = CreateConVar("sm_realismpenfix_magnumlimit", "0.0", "Maximum number of enemies one magnum bullet can kill (0 disables feature)", CVAR_FLAGS);
  55.     new Handle:cvarSniperLimit = CreateConVar("sm_realismpenfix_sniperlimit", "0.0", "Maximum number of enemies one sniper bullet can kill (0 disables feature)", CVAR_FLAGS);
  56.     CreateConVar("sm_realismpenfix_ver", PLUGIN_VERSION, PLUGIN_NAME, CVAR_FLAGS|FCVAR_DONTRECORD);
  57.  
  58.     AutoExecConfig(true, "l4d2_realismpenfix");
  59.  
  60.     HookConVarChange(cvarEnable, OnRPFEnableChanged);
  61.     HookConVarChange(cvarDebug, OnRPFDebugChanged);
  62.     HookConVarChange(cvarNerfMagnum, OnNerfMagnumChanged);
  63.     HookConVarChange(cvarBuffSniper, OnBuffSniperChanged);
  64.     HookConVarChange(cvarMagnumLimit, OnMagnumLimitChanged);
  65.     HookConVarChange(cvarSniperLimit, OnSniperLimitChanged);
  66.  
  67.     // get cvars after AutoExecConfig
  68.     g_iEnabled = GetConVarInt(cvarEnable);
  69.     g_bDebug = GetConVarBool(cvarDebug);
  70.     g_iNerfMagnum = GetConVarInt(cvarNerfMagnum);
  71.     g_iBuffSniper = GetConVarInt(cvarBuffSniper);
  72.     g_iMagnumPenetrationLimit = GetConVarInt(cvarMagnumLimit);
  73.     g_iSniperPenetrationLimit = GetConVarInt(cvarSniperLimit);
  74.    
  75.     HookEvent("weapon_fire", Event_WeaponFire);
  76.     if (g_bDebug)
  77.     {
  78.         HookEvent("infected_hurt", Event_InfectedHurt);
  79.     }
  80.    
  81.     new Handle:difficulty = FindConVar("z_difficulty");
  82.     HookConVarChange(difficulty, OnDifficultyChanged);
  83.     decl String:difficultyString[32];
  84.     GetConVarString(difficulty, difficultyString, sizeof(difficultyString));
  85.     g_fDifficultyMultiplier = GetDifficultyMultiplier(difficultyString);
  86.    
  87.     new Handle:gamemode = FindConVar("mp_gamemode");
  88.     HookConVarChange(gamemode, OnGameModeChanged);
  89.     decl String:gamemodeString[32];
  90.     GetConVarString(gamemode, gamemodeString, sizeof(gamemodeString));
  91.     g_bRealism = isGameModeRealism(gamemodeString);
  92. }
  93.  
  94.  
  95. stock Require_L4D2()
  96. {
  97.     decl String:game[32];
  98.     GetGameFolderName(game, sizeof(game));
  99.     if (!StrEqual(game, "left4dead2", false))
  100.     {
  101.         SetFailState("Plugin supports Left 4 Dead 2 only.");
  102.     }
  103. }
  104.  
  105. public OnGameModeChanged(Handle:convar, const String:oldValue[], const String:newValue[])
  106. {
  107.     g_bRealism = isGameModeRealism(newValue);
  108.     if (g_bDebug)
  109.     {
  110.         if (g_bRealism) PrintToChatAll("Realism");
  111.         else PrintToChatAll("Not realism");
  112.        
  113.     }
  114. }
  115.  
  116. public OnDifficultyChanged(Handle:convar, const String:oldValue[], const String:newValue[])
  117. {
  118.     g_fDifficultyMultiplier = GetDifficultyMultiplier(newValue);
  119.     if (g_bDebug) PrintToChatAll("Difficulty multiplier %f", g_fDifficultyMultiplier);
  120.  
  121. }
  122.  
  123. public bool:isGameModeRealism(const String:GameMode[])
  124. {
  125.     return (StrContains(GameMode, "realism", false) >= 0);
  126. }
  127.  
  128. public Float:GetDifficultyMultiplier(const String:Difficulty[])
  129. {
  130.     new Float:ret = 1.0;
  131.     if (g_bRealism)
  132.     {
  133.         if (StrEqual(Difficulty, "impossible", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_expert"));
  134.         if (StrEqual(Difficulty, "hard", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_expert"));
  135.         if (StrEqual(Difficulty, "normal", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_hard"));
  136.         if (StrEqual(Difficulty, "easy", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_normal"));
  137.     }
  138.     else
  139.     {
  140.         if (StrEqual(Difficulty, "impossible", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_expert"));
  141.         if (StrEqual(Difficulty, "hard", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_hard"));
  142.         if (StrEqual(Difficulty, "normal", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_normal"));
  143.         if (StrEqual(Difficulty, "easy", false)) ret = GetConVarFloat(FindConVar("z_non_head_damage_factor_easy"));
  144.     }
  145.    
  146.     ret *= GetConVarFloat(FindConVar("z_non_head_damage_factor_multiplier"));
  147.     return ret;
  148. }
  149.  
  150. public OnNerfMagnumChanged(Handle:cvar, const String:oldVal[], const String:newVal[])
  151. {
  152.     g_iNerfMagnum = StringToInt(newVal);
  153. }
  154.  
  155. public OnBuffSniperChanged(Handle:cvar, const String:oldVal[], const String:newVal[])
  156. {
  157.     g_iBuffSniper = StringToInt(newVal);
  158. }
  159.  
  160. public OnMagnumLimitChanged(Handle:cvar, const String:oldVal[], const String:newVal[])
  161. {
  162.     g_iMagnumPenetrationLimit = StringToInt(newVal);
  163. }
  164.  
  165. public OnSniperLimitChanged(Handle:cvar, const String:oldVal[], const String:newVal[])
  166. {
  167.     g_iSniperPenetrationLimit = StringToInt(newVal);
  168. }
  169.  
  170. public OnRPFEnableChanged(Handle:cvar, const String:oldVal[], const String:newVal[])
  171. {
  172.     g_iEnabled = StringToInt(newVal);
  173. }
  174.  
  175. public OnRPFDebugChanged(Handle:cvar, const String:oldVal[], const String:newVal[])
  176. {
  177.     g_bDebug = StringToInt(newVal) == 1;
  178.     if (g_bDebug)
  179.     {
  180.         HookEvent("infected_hurt", Event_InfectedHurt);
  181.     }
  182.     else
  183.     {
  184.         UnhookEvent("infected_hurt", Event_InfectedHurt);
  185.     }
  186. }
  187.  
  188. public OnEntityCreated(entity, const String:classname[])
  189. {
  190.     if (entity <= MaxClients || entity > 2048) return;
  191.  
  192.     if (StrEqual(classname, "infected"))
  193.     {
  194.         SDKHook(entity, SDKHook_SpawnPost, RealismPenetrationFix_SpawnPost);
  195.     }
  196. }
  197.  
  198. public Action:Event_InfectedHurt(Handle:event, const String:name[], bool:dontBroadcast)
  199. {
  200.     if (g_iEnabled > 0 && g_bDebug)
  201.     {
  202.         new entityid = GetEventInt(event, "entityid");
  203.         PrintToChatAll("IH: Hit infected in the %d for %d damage (%d health)", GetEventInt(event, "hitgroup"), GetEventInt(event, "amount"), GetEntProp(entityid, Prop_Data, "m_iHealth"));
  204.     }
  205. }
  206.  
  207. // Model name does not exist until after the uncommon is spawned
  208. public RealismPenetrationFix_SpawnPost(entity)
  209. {
  210.     SDKHook(entity, SDKHook_TraceAttack, RealismPenetrationFixTraceAttack);
  211.     SDKHook(entity, SDKHook_OnTakeDamage, RealismPenetrationFixOnTakeDamage);
  212. }
  213.  
  214. new Float:lastTraceAttackDamage = 0.0;
  215. public Action:RealismPenetrationFixTraceAttack(victim, &attacker, &inflictor, &Float:damage, &damagetype, &ammotype, hitbox, hitgroup)
  216. {
  217.     if (g_bDebug)
  218.     {
  219.         decl String:victimName[MAX_TARGET_LENGTH] = "Unconnected";
  220.         decl String:attackerName[MAX_TARGET_LENGTH] = "Unconnected";
  221.         decl String:inflictorName[32] = "Invalid";
  222.        
  223.         if (victim > 0 && victim <= MaxClients && IsClientConnected(victim))        GetClientName(victim, victimName, sizeof(victimName));
  224.         else if (IsValidEntity(victim))                                             GetEntityClassname(victim, victimName, sizeof(victimName));
  225.        
  226.         if (attacker > 0 && attacker <= MaxClients && IsClientConnected(attacker))  GetClientName(attacker, attackerName, sizeof(attackerName));
  227.         else if (IsValidEntity(attacker))                                           GetEntityClassname(attacker, attackerName, sizeof(attackerName));
  228.        
  229.         if (inflictor > 0 && IsValidEntity(inflictor))                              GetEntityClassname(inflictor, inflictorName, sizeof(inflictorName));
  230.        
  231.         PrintToChatAll("TA: %s hit %s in the %d / %d with %s / %x / %d for %.2f", attackerName, victimName, hitbox, hitgroup, inflictorName, damagetype, ammotype, damage);
  232.     }
  233.     /*
  234.     // was trying to see if I could make magnum bullets have deeper penetration
  235.     if (ammotype == 2)
  236.     {
  237.         ammotype = 10;
  238.         return Plugin_Changed;
  239.     }
  240.     */
  241.     lastTraceAttackDamage = damage;
  242.     return Plugin_Continue;
  243. }
  244.  
  245. public Action:RealismPenetrationFixOnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype, &weapon, Float:damageForce[3], Float:damagePosition[3])
  246. {
  247.     new bool:changed = false;
  248.     new bool:bModeEnabled = (g_iEnabled == 2) || (g_iEnabled == 1 && g_bRealism);
  249.     decl String:weaponName[32] = "Invalid";
  250.    
  251.     // Are we enabled?
  252.     // Is attacker a Survivor that is alive?
  253.     if (bModeEnabled && IS_SURVIVOR_ALIVE(attacker))
  254.     {
  255.         g_iCurrentPenetrationCount[attacker]++;
  256.         if (weapon > 0 && IsValidEntity(weapon))
  257.         {
  258.             GetEntityClassname(weapon, weaponName, sizeof(weaponName));
  259.         }
  260.        
  261.         // If not realism, is sniper being nerfed?
  262.         // If realism, is sniper being buffed?
  263.         // If any gamemode, is magnum being nerfed?
  264.         // Did the attacker NOT do a head shot? (damagetype 0x40000000)
  265.         if (((!g_bRealism && g_iBuffSniper < 2) || (g_bRealism && g_iBuffSniper > 0) || g_iNerfMagnum > 0) && !(damagetype & 0x40000000))
  266.         {      
  267.             new bool:bNerfMag = g_iNerfMagnum > 0, bMagStumble = true;
  268.            
  269.             if ((bNerfMag || bMagStumble) && StrEqual(weaponName, "weapon_pistol_magnum"))
  270.             {
  271.                 if (bNerfMag)
  272.                 {                  
  273.                     if (g_iNerfMagnum > 2)
  274.                     {
  275.                         // 3: no penetration bonus, distance penalty
  276.                         damage = g_fDifficultyMultiplier * lastTraceAttackDamage;
  277.                     }
  278.                     else if (g_iNerfMagnum > 1)
  279.                     {
  280.                         // 2: no penetration bonus, no distace penalty
  281.                         damage = g_fDifficultyMultiplier * 78.0;
  282.                     } else {
  283.                         // 1: half penetration bonus
  284.                         damage /= 2.0;
  285.                     }
  286.                     changed = true;
  287.                 }
  288.                 if (bMagStumble)
  289.                 {
  290.                     // TODO: get pos of infected and survivor, TeleportEntity the infected in that direction with some amount of velocity
  291.                     // TODO: SDKCall InfectedShoved::OnShoved(Infected *, CBaseEntity *)?  ScaleVector damageForce by something ridiculous?
  292.                 }
  293.             }
  294.             else if (((!g_bRealism && g_iBuffSniper < 2) || (g_bRealism && g_iBuffSniper > 0)) && (StrEqual(weaponName, "weapon_hunting_rifle") || StrEqual(weaponName, "weapon_sniper_military")))
  295.             {
  296.                 new Float:maxHealth = float(GetEntProp(victim, Prop_Data, "m_iMaxHealth"));
  297.                
  298.                 // Penetration bonus is 1/2 for Fallen Survivor
  299.                 if (isFallenSurvivor(victim)) maxHealth /= 2.0;
  300.  
  301.                 new Float:newDamage = damage;
  302.                
  303.                 if (g_bRealism)
  304.                 {
  305.                     if (g_iBuffSniper > 1)
  306.                     {
  307.                         // 2: full penetration bonus
  308.                         newDamage = maxHealth;
  309.                     } else {
  310.                         // 1: half penetration bonus
  311.                         newDamage = maxHealth/2.0;
  312.                     }
  313.                     if (newDamage > damage) damage = newDamage;
  314.                     changed = true;
  315.                 }
  316.                 else if (!g_bRealism)
  317.                 {
  318.                     if (g_iBuffSniper < 1)
  319.                     {
  320.                         // 0: No penetration bonus
  321.                         damage = g_fDifficultyMultiplier * damage;
  322.                     }
  323.                     else
  324.                     {
  325.                         // 1: half penetration bonus
  326.                         newDamage = maxHealth/2.0;
  327.                     }
  328.                     if (newDamage < damage) damage = newDamage;
  329.                     changed = true;
  330.                 }
  331.             }
  332.         }
  333.         if (g_bDebug)
  334.         {
  335.             decl String:victimName[MAX_TARGET_LENGTH] = "Unconnected";
  336.             decl String:attackerName[MAX_TARGET_LENGTH] = "Unconnected";
  337.             decl String:inflictorName[32] = "Invalid";
  338.            
  339.             if (victim > 0 && victim <= MaxClients && IsClientConnected(victim))        GetClientName(victim, victimName, sizeof(victimName));
  340.             else if (IsValidEntity(victim))                                             GetEntityClassname(victim, victimName, sizeof(victimName));
  341.            
  342.             if (attacker > 0 && attacker <= MaxClients && IsClientConnected(attacker))  GetClientName(attacker, attackerName, sizeof(attackerName));
  343.             else if (IsValidEntity(attacker))                                           GetEntityClassname(attacker, attackerName, sizeof(attackerName));
  344.            
  345.             if (inflictor > 0 && IsValidEntity(inflictor))                              GetEntityClassname(inflictor, inflictorName, sizeof(inflictorName));
  346.            
  347.             PrintToChatAll("OTD: %s hit %s %d with %s / %s / %x for %.2f [%.2f]", attackerName, victimName, g_iCurrentPenetrationCount[attacker], weaponName, inflictorName, damagetype, damage, GetVectorLength(damageForce));
  348.         }
  349.         if (g_iMagnumPenetrationLimit > 0 && g_iCurrentPenetrationCount[attacker] > g_iMagnumPenetrationLimit && StrEqual(weaponName, "weapon_pistol_magnum"))
  350.         {
  351.             return Plugin_Handled;
  352.         }
  353.         if (g_iSniperPenetrationLimit > 0 && g_iCurrentPenetrationCount[attacker] > g_iSniperPenetrationLimit && (StrEqual(weaponName, "weapon_hunting_rifle") || StrEqual(weaponName, "weapon_sniper_military")))
  354.         {
  355.             return Plugin_Handled;
  356.         }
  357.     }
  358.     if (changed) return Plugin_Changed;
  359.     else return Plugin_Continue;
  360. }
  361.  
  362. public Action:Event_WeaponFire(Handle:event, const String:name[], bool:dontBroadcast)
  363. {
  364.     new client = GetClientOfUserId(GetEventInt(event, "userid"));
  365.     g_iCurrentPenetrationCount[client] = 0;
  366. }
  367.  
  368. stock bool:isFallenSurvivor(entity)
  369. {
  370.     if (entity <= 0 || entity > 2048 || !IsValidEdict(entity)) return false;
  371.     decl String:model[128];
  372.     GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model));
  373.     return StrContains(model, "fallen") != -1; // Common is a fallen uncommon
  374. }
Advertisement
Add Comment
Please, Sign In to add comment