PLUGIN.name = "Stamina" PLUGIN.author = "Chessnut" PLUGIN.description = "Adds a stamina system to limit running." -- luacheck: push ignore 631 ix.config.Add("staminaDrain", 1, "How much stamina to drain per tick (every quarter second). This is calculated before attribute reduction.", nil, { data = {min = 0, max = 10, decimals = 2}, category = "characters" }) ix.config.Add("staminaRegeneration", 1.75, "How much stamina to regain per tick (every quarter second).", nil, { data = {min = 0, max = 10, decimals = 2}, category = "characters" }) ix.config.Add("staminaCrouchRegeneration", 2, "How much stamina to regain per tick (every quarter second) while crouching.", nil, { data = {min = 0, max = 10, decimals = 2}, category = "characters" }) ix.config.Add("punchStamina", 10, "How much stamina punches use up.", nil, { data = {min = 0, max = 100}, category = "characters" }) -- luacheck: pop local function CalcStaminaChange(client) local character = client:GetCharacter() if (!character) then return 0 end -- Если в технике или в ноуклипе - сразу реген и выходим local inVehicle = client:InVehicle() or IsValid(client:GetVehicle()) if (not inVehicle and client.lvsGetVehicle) then local v = client:lvsGetVehicle() inVehicle = IsValid(v) end if (not inVehicle and IsValid(client:GetParent()) and client:GetParent():IsVehicle()) then inVehicle = true end local isNoClip = client:GetMoveType() == MOVETYPE_NOCLIP if (inVehicle or isNoClip) then local offset = ix.config.Get("staminaRegeneration", 1.75) if (SERVER) then local current = client:GetLocalVar("stm", 0) client:SetLocalVar("stm", math.Clamp(current + offset, 0, 100)) end return offset end local walkSpeed = ix.config.Get("walkSpeed") local maxAttributes = ix.config.Get("maxAttributes", 100) local offset if (client:KeyDown(IN_SPEED) and client:GetVelocity():LengthSqr() >= 5000 and client:OnGround()) then -- characters could have attribute values greater than max if the config was changed offset = -ix.config.Get("staminaDrain", 1) + math.min(character:GetAttribute("end", 0), maxAttributes) / 100 -- Ensure minimum drain if (offset > -0.2) then offset = -0.2 end -- Armor weight influence (built-in fix) local armor = client:Armor() if (armor > 0) then offset = offset * (1 + (math.min(armor, 100) / 100) * 0.7) end else offset = client:Crouching() and ix.config.Get("staminaCrouchRegeneration", 2) or ix.config.Get("staminaRegeneration", 1.75) -- Armor slows down regeneration but doesn't cause drain while standing local armor = client:Armor() if (armor > 0 and offset > 0) then offset = offset * math.max(1 - (math.min(armor, 100) / 100) * 0.5, 0.1) end -- Гарантируем, что регенерация это регенерация (не меньше нуля) offset = math.max(offset, 0.05) end offset = hook.Run("AdjustStaminaOffset", client, offset) or offset if (CLIENT) then return offset -- for the client we need to return the estimated stamina change else local current = client:GetLocalVar("stm", 0) local value = math.Clamp(current + offset, 0, 100) if (current != value) then client:SetLocalVar("stm", value) if (value == 0 and !client:GetNetVar("brth", false)) then client:SetNetVar("brth", true) character:UpdateAttrib("end", 0.1) character:UpdateAttrib("stm", 0.01) hook.Run("PlayerStaminaLost", client) elseif (value >= 50 and client:GetNetVar("brth", false)) then client:SetNetVar("brth", nil) hook.Run("PlayerStaminaGained", client) end end end end function PLUGIN:SetupMove(client, mv, cmd) if (client:GetNetVar("brth", false)) then mv:SetMaxClientSpeed(client:GetWalkSpeed()) end end if (SERVER) then function PLUGIN:PostPlayerLoadout(client) local uniqueID = "ixStam" .. client:SteamID() timer.Create(uniqueID, 0.25, 0, function() if (!IsValid(client)) then timer.Remove(uniqueID) return end CalcStaminaChange(client) end) end function PLUGIN:CharacterPreSave(character) local client = character:GetPlayer() if (IsValid(client)) then character:SetData("stamina", client:GetLocalVar("stm", 0)) end end function PLUGIN:PlayerLoadedCharacter(client, character) timer.Simple(0.25, function() client:SetLocalVar("stm", character:GetData("stamina", 100)) end) end local playerMeta = FindMetaTable("Player") function playerMeta:RestoreStamina(amount) local current = self:GetLocalVar("stm", 0) local value = math.Clamp(current + amount, 0, 100) self:SetLocalVar("stm", value) end function playerMeta:ConsumeStamina(amount) local current = self:GetLocalVar("stm", 0) local value = math.Clamp(current - amount, 0, 100) self:SetLocalVar("stm", value) end else local predictedStamina = 100 function PLUGIN:Think() local offset = CalcStaminaChange(LocalPlayer()) -- the server check it every 0.25 sec, here we check it every [FrameTime()] seconds offset = math.Remap(FrameTime(), 0, 0.25, 0, offset) if (offset != 0) then predictedStamina = math.Clamp(predictedStamina + offset, 0, 100) end end function PLUGIN:OnLocalVarSet(key, var) if (key != "stm") then return end if (math.abs(predictedStamina - var) > 5) then predictedStamina = var end end ix.bar.Add(function() return predictedStamina / 100 end, Color(200, 200, 40), nil, "stm") end