local Movespeed_Reduction_Table = { [TF_CLASS_SCOUT] = 120 / 300, [TF_CLASS_SOLDIER] = 120 / 240, [TF_CLASS_PYRO] = 120 / 270, [TF_CLASS_DEMOMAN] = 120 / 240, [TF_CLASS_HEAVYWEAPONS] = 120 / 240, [TF_CLASS_ENGINEER] = 120 / 220, [TF_CLASS_MEDIC] = 120 / 270, [TF_CLASS_SNIPER] = 120 / 200, [TF_CLASS_SPY] = 120 / 240 } local Revive_Tickrate = 0.1 local Revive_Delay = 0 local Revive_Duration = 6 / Revive_Tickrate function RealScreenRecoil( projectile, activator, weapon ) local recoil = Vector( weapon:GetAttributeValue( "wrench index" ) or 0, 0, 0 ) local eye_angle = activator:GetEyeAngles() if activator.b_IsDowned then recoil = recoil * 2; end --print(activator.bRealisticRecoilEnabled) if not activator.bRealisticRecoilEnabled then return end activator:SnapEyeAngles( activator:GetEyeAngles() + recoil ) --terrible with ping end local cmd_table_realistic_recoil = { "!RealisticRecoil", "/RealisticRecoil", "!Realistic_Recoil", "/Realistic_Recoil", "!Recoil", "/Recoil" } local dmg_table_force_down = { "!Down", "/Down" } function FixPlayerChat(event_table) local text = event_table.text local userid = event_table.userid local player = ents.GetPlayerByUserId(userid) for s,_ in pairs(cmd_table_realistic_recoil) do if ( string.match(string.lower(text), string.lower(cmd_table_realistic_recoil[s])) ) then if not player.bRealisticRecoilEnabled then util.PrintToChat( player, "Realistic Recoil is now enabled!" ) player.bRealisticRecoilEnabled = true else util.PrintToChat( player, "Realistic Recoil is now disable!" ) player.bRealisticRecoilEnabled = false end break end end -- for s,_ in pairs(dmg_table_force_down) do -- if ( string.match(string.lower(text), string.lower(dmg_table_force_down[s])) ) then -- if not player.b_IsDowned then -- player.DownedProgress = 0 -- player.b_IsDowned = true -- player:AddHealth( 500 - player.m_iHealth, true ) -- else -- player.DownedProgress = 0 -- player.b_IsDowned = false -- player.m_iHealth = 50 * ( player.m_iHealth / 500 ) -- end -- break -- end -- end end CEntity.DownPlayer = function( self ) if not IsValidAliveRealPlayer( self ) then return end self.DownedProgress = 0 self.b_IsDowned = true self:AddHealth( 500 - self.m_iHealth, true ) self.iDowns = self.iDowns + 1 end function OnGameTick() for _, player in pairs( ents.GetAllPlayers() ) do if player:IsRealPlayer() then local iMaxHealth = ( player.m_iMaxHealth + ( math.min( player.m_iDecapitations, 4 ) * 15 ) ) if not player.b_IsDowned then player:SetAttributeValue("major move speed bonus", lerp( 1, Movespeed_Reduction_Table[player.m_iClass], math.clamp( 1 - ( player.m_iHealth / iMaxHealth ), 0, 1 ) ) ) player:SetAttributeValue( "mult duck speed", nil ) player:SetAttributeValue( "fire rate penalty", nil ) player:SetAttributeValue( "dmg taken increased", nil ) player:SetAttributeValue( "no_attack", nil ) player:SetAttributeValue( "no_jump", nil ) if IsValid(player.m_hActiveWeapon) and ( string.match( player.m_hActiveWeapon:GetItemName(), "Maxine Gun" ) ) then if player:InCond( TF_COND_AIMING ) then if player.RevTime == false then player.RevTime = CurTime() else local attr = lerp( 1, 0.25, math.clamp( ( CurTime() - player.RevTime ) / 10, 0, 1 ) ) player.m_hActiveWeapon:SetAttributeValue( "fire rate penalty HIDDEN", attr ) end else player.RevTime = false player.m_hActiveWeapon:SetAttributeValue( "fire rate penalty HIDDEN", nil ) end end else player:SetAttributeValue( "dmg taken increased", 0.5 ) if not player.b_IsCrouching then player:SetAttributeValue( "major move speed bonus", 0.001337 ) player:SetAttributeValue( "no_attack", 1 ) player:SetAttributeValue( "no_jump", 1 ) player:SetAttributeValue( "mult duck speed", nil ) player:SetAttributeValue( "fire rate penalty", nil ) player:Print( 2, "You are Downed! \nCrouch to be able to move and shoot!" ) else player:SetAttributeValue( "major move speed bonus", 0.2 ) player:SetAttributeValue( "mult duck speed", 4 ) player:SetAttributeValue( "no_attack", nil ) player:Print( 2, "You are Downed!" ) if player:GetActivePlayerWeapon() ~= player:GetPlayerItemBySlot( 2 ) then player:SetAttributeValue( "fire rate penalty", 4 ) end end local text_length = 12 local message = "Revive Progress! " local color = { 200, 50, 50 } local time_remaining = player.DownedProgress local time_remaining_percent = 1 - ( time_remaining / Revive_Duration ) local message_percent = 1.0 / text_length local recharge_meter = math.ceil( time_remaining_percent / message_percent ) for i = 1, ( text_length - recharge_meter ) do message = message .. "▰" text_length = text_length - 1 end for i = 1, text_length do message = message .. "▱" end player:ShowHudText({channel = 2, y = 0.6, x = -0.855 + ( string.len( message ) / 106 ), r1 = color[1], r2 = color[1], g1 = color[2], g2 = color[2], b1 = color[3], b2 = color[3], effect = 1, fixTime = 0, fadeinTime = 0, fadeoutTime = 0, holdTime = 0.5 }, message ) player:StunPlayer( 0.1, 0, TF_STUNFLAG_SLOWDOWN ) if IsValid( player:GetPlayerItemBySlot( 0 ) ) then if player:GetActivePlayerWeapon() == player:GetPlayerItemBySlot( 0 ) then player:WeaponSwitchSlot( 2 ) player:WeaponSwitchSlot( 1 ) end end end if CurTime() >= Revive_Delay then local found = false for _, oth in pairs( ents.GetAllPlayers() ) do if ( not found ) and ( oth ~= player ) and ( not player.b_IsDowned ) and oth:IsValid() and oth:IsRealPlayer() and oth:IsAlive() and oth.b_IsDowned and oth.b_IsCrouching and ( player:GetAbsOrigin():Distance( oth:GetAbsOrigin() ) <= 128 ) then local message = "Reviving " .. tostring( oth:GetPlayerName() ) .. "! " found = true if not tobool( player.m_bUsingActionSlot ) then player:Print( 2, "Hold your action key to revive " .. tostring( oth:GetPlayerName() ) .. "!" ) oth.DownedProgress = math.max( oth.DownedProgress - 3, 0 ) message = "Revive " .. tostring( oth:GetPlayerName() ) .. "! " player:SetAttributeValue( "no_attack", nil ) player:SetAttributeValue( "no_jump", nil ) player:SetAttributeValue( "major move speed bonus", nil ) player.HelpingPlayer = nil else message = "Reviving " .. tostring( oth:GetPlayerName() ) .. "! " oth.DownedProgress = oth.DownedProgress + 1 player.HelpingPlayer = oth local angle = player:GetEyePos():FaceVector( oth:GetEyePos() ) player:SnapEyeAngles( angle ) if oth.DownedProgress >= Revive_Duration then oth.b_IsDowned = false oth.DownedProgress = 0 oth.m_iHealth = 50 * ( oth.m_iHealth / 500 ) oth:Print( 2, "" ) player.iRevives = player.iRevives + 1 player.HelpingPlayer = nil end player:SetAttributeValue( "no_attack", 1 ) player:SetAttributeValue( "no_jump", 1 ) player:SetAttributeValue( "major move speed bonus", 0.4 ) end local text_length = 12 local color = { 200, 50, 50 } local time_remaining = oth.DownedProgress local time_remaining_percent = 1 - ( time_remaining / Revive_Duration ) local message_percent = 1.0 / text_length local recharge_meter = math.ceil( time_remaining_percent / message_percent ) for i = 1, ( text_length - recharge_meter ) do message = message .. "▰" text_length = text_length - 1 end for i = 1, text_length do message = message .. "▱" end player:ShowHudText({channel = 2, y = 0.7, x = -0.855 + ( string.len( message ) / 106 ), r1 = color[1], r2 = color[1], g1 = color[2], g2 = color[2], b1 = color[3], b2 = color[3], effect = 1, fixTime = 0, fadeinTime = 0, fadeoutTime = 0, holdTime = 0.5 }, message ) end end end end end if CurTime() >= Revive_Delay then Revive_Delay = CurTime() + Revive_Tickrate end end local Weapon_Data_Table = { ["MP40"] = { AmmoType = 1, MaxAmmo = 200 }, [".357 Magnum"] = { AmmoType = 1, MaxAmmo = 48 }, ["AK 47"] = { AmmoType = 1, MaxAmmo = 320 }, ["Paratropper's Rifle"] = { AmmoType = 1, MaxAmmo = 240 }, ["G36"] = { AmmoType = 1, MaxAmmo = 144 }, ["Tartarus"] = { AmmoType = 1, MaxAmmo = 72 }, ["LMG"] = { AmmoType = 1, MaxAmmo = 384 }, ["HMG"] = { AmmoType = 1, MaxAmmo = 250 }, ["Hellfire"] = { AmmoType = 1, MaxAmmo = 288 }, } function OnPlayerConnected( player ) if player:IsRealPlayer() then player.bRealisticRecoilEnabled = false player.b_IsDowned = false player.b_IsCrouching = false player.iDowns = 0 player.iDeaths = 0 player.iKills = 0 player.iAssists = 0 player.iRevives = 0 player.RevTime = false player:AddCallback( ON_SPAWN, function( ent ) ent.b_IsDowned = false ent.b_IsCrouching = false ent.RevTime = false timer.Simple(0.5, function() ent:Regenerate() --fix weird ammo bugs end) if IsValid( ent:GetPlayerItemBySlot(0) ) then for name, v in pairs( Weapon_Data_Table ) do --print( name, ent:GetPlayerItemBySlot(0):GetItemName(), string.match( ent:GetPlayerItemBySlot(0):GetItemName(), name ) ) if string.match( ent:GetPlayerItemBySlot(0):GetItemName(), name ) then ent:GetPlayerItemBySlot(0).m_iPrimaryAmmoType = Weapon_Data_Table[name].AmmoType ent.m_iAmmo[Weapon_Data_Table[name].AmmoType + 1] = Weapon_Data_Table[name].MaxAmmo end end end end) player:AddCallback( ON_DAMAGE_RECEIVED_PRE, function( ent, dmginfo ) if IsValid( ent.HelpingPlayer ) then ent.HelpingPlayer.DownedProgress = math.max( ent.HelpingPlayer.DownedProgress - 20, 0 ) end if ent.b_IsDowned then ent.DownedProgress = math.max( ent.DownedProgress - 15, 0 ) end end) player:AddCallback( ON_DEATH, function( ent ) if not player.b_IsDowned then player.OldPos = player:GetAbsOrigin() timer.Simple( 0.1, function() player:ForceRespawn() player:SetAbsOrigin( player.OldPos ) player:DownPlayer() player:StunPlayer( 0.5, 1, TF_STUNFLAG_BONKSTUCK ) end) else player.b_IsDowned = false player.iDeaths = player.iDeaths + 1 end end) player:AddCallback(ON_KEY_PRESSED, function(entity, key) if key == IN_DUCK then player.b_IsCrouching = true end end) player:AddCallback(ON_KEY_RELEASED, function(entity, key) if key == IN_DUCK then player.b_IsCrouching = false end end) end end AddEventCallback("player_say", FixPlayerChat) --[[ AI DIRECTOR SECTION Goal: Calculate which player to target in order from best to worst based on kills, downs, deaths, and revives. Also take in account both the bot's and player's weapons for both threat value and engaging distance. Spawn an info_target on each player's head to serve as the InterruptAction target. Take different rolls depending on number of bots. EX: Say there are 7 bots, 4 are attacking, 3 can be more defensive or play sniper. Dynamically find Defensive points. Find points that are farther than the bot would normally attack from, find a spot outside of the target player's LoS. Try to stick to 25-60% farther away than normal for hold point Join squads based on number of bots and which target the bot is going after (threat level of the targeted player) Desired_Distance = PlayerDist - ( ( PlayerDist - BotDist ) / BotDist_Weight ) Weight = 2 means half way perfectly Weight cannot go below 1, otherwise weirdness will occur Low weight means stick to BotDist High weight means stick to PlayerDist +----------------+-----------+--------------+------------+ | PlayDist = 100 | LowWeight | MediumWeight | HighWeight | | | 1.1 | 2 | 5 | +----------------+-----------+--------------+------------+ | LowBotDist | 190 | 150 | 120 | | 200 | | | | +----------------+-----------+--------------+------------+ | MediumBotDist | 554 | 350 | 200 | | 600 | | | | +----------------+-----------+--------------+------------+ | HighBotDist | 918 | 550 | 280 | | 1000 | | | | +----------------+-----------+--------------+------------+ +-----------------+-----------+--------------+------------+ | PlayDist = 1500 | LowWeight | MediumWeight | HighWeight | | | 1.1 | 2 | 5 | +-----------------+-----------+--------------+------------+ | LowBotDist | 318 | 850 | 1240 | | 200 | | | | +-----------------+-----------+--------------+------------+ | MediumBotDist | 681 | 1050 | 1320 | | 600 | | | | +-----------------+-----------+--------------+------------+ | HighBotDist | 1045 | 1250 | 1400 | | 1000 | | | | +-----------------+-----------+--------------+------------+ --]] local AI_Kill_AddThreat = 2 local AI_Assist_AddThreat = 1 local AI_Revives_AddThreat = 10 local AI_Down_AddThreat = -8 local AI_Deaths_AddThreat = -20 local AI_Desired_Attackers = 2 --change dynamically based on number of bots, number of players, and threat levels local AI_Desired_Protectors = 2 --change dynamically based on number of bots, players, and attacker bots. Take more defensive positions/be more laid back and not a front liner, first to be front liners if need be local AI_Desired_Snipers = 1 --change dynamically based on number of bots, players, attacker bots, and protector bots. Take more defensive back line positions, last to be front liners local AI_Max_Squad_Size = 3 --how many attackers and protectors can be in one group local AI_Max_Squad_Distance = 300 --how far squad members can be from the leader local AI_Wonder_Max_Dist = 500 --max distance a bot can wonder from it's protection point local AI_Wonder_Max_Height_Dif = 64 --max height diff the floor of the wonder point be from protection point local AI_Wonder_Min_Height_Dif = -200 --min height diff the floor of the wonder point be from protection point local AI_PlayerWeaponThreatTable = { ["[Light] Glock"] = { Threat_mult = 0.5, Distance = 600 } } local AI_BotWeaponThreatTable = { ["[Light] Glock"] = { Distance = 400, Distance_Weight = 1.5 } } function AI_ReturnDesiredDistance( bot, player ) local Player_Data = AI_PlayerWeaponThreatTable[player.m_hActiveWeapon:GetItemName()] local Bot_Data = AI_BotWeaponThreatTable[bot.m_hActiveWeapon:GetItemName()] return Player_Data.Distance - ( ( Player_Data.Distance - Bot_Data.Distance ) / Bot_Data.Distance_Weight ) end function AI_GetPlayerThreat( player ) local ThreatLevel = player.iKills * AI_Kill_AddThreat ThreatLevel = ThreatLevel + ( player.iAssists * AI_Assist_AddThreat ) ThreatLevel = ThreatLevel + ( player.iRevives * AI_Revives_AddThreat ) ThreatLevel = ThreatLevel + ( player.iDowns * AI_Down_AddThreat ) ThreatLevel = ThreatLevel + ( player.iDeaths * AI_Deaths_AddThreat ) for i = 0, 2 do if IsValid( player:GetPlayerItemBySlot( i ) ) then ThreatLevel = ThreatLevel * ( AI_PlayerWeaponThreatTable[player:GetPlayerItemBySlot( i ):GetItemName()] ).Threat_mult end end return ThreatLevel end -- function Test( _, activator ) -- activator:RunScriptCode( "EntFire( `popscript`, `$Capture`, self.CanDuck() )" ) -- end -- function Capture( check ) -- print( 'capture', check ) -- end