```lua
-- cl_realistic_smoke.lua

if CLIENT then
    ---------------------------------------------------
    -- Settings and Constants
    ---------------------------------------------------
    local SmokeSettings = {
        EmitRate = 10,              -- Количество частиц, эмитируемых за кадр на эмиттер
        MaxParticles = 1000,        -- Максимальное количество частиц
        Spread = 50,                -- Распределение начальной позиции (в пределах EmitVolume)
        InitialVelocity = Vector(0, 0, 50),  -- Начальная скорость частиц (вверх)
        VelocityRandomness = 20,    -- Случайность скорости
        ParticleLifetime = {4, 8},  -- Диапазон времени жизни частиц (секунды)
        ParticleSize = {15, 40},    -- Диапазон начального размера частиц (единицы)
        FadeOutTime = 2,            -- Время затухания (секунды)
        ColorStart = Color(120, 120, 120, 255), -- Начальный цвет
        ColorEnd = Color(80, 80, 80, 0),         -- Конечный цвет
        Texture = "particle/particle_smokegrenade", -- Текстура частиц
        Buoyancy = 20,              -- Восходящее ускорение (плавучесть)
        Turbulence = 10,            -- Интенсивность турбулентности
        Wind = Vector(10, 0, 0),    -- Направление и сила ветра
        EmitVolume = {              -- Объем, в пределах которого частицы могут появляться относительно эмиттера
            min = Vector(-100, -100, 0),
            max = Vector(100, 100, 100)
        },
        DensityMultiplier = 1,      -- Множитель плотности
        TemperatureEffect = {
            BuoyancyMultiplier = 1, -- Множитель плавучести в зависимости от температуры
            Stability = 1           -- Параметр атмосферной стабильности (от -1 до 1)
        },
        HumidityEffect = {
            OpacityMultiplier = 1   -- Множитель прозрачности в зависимости от влажности
        },
    }

    local DebugMode = false

    -- Предварительная загрузка текстуры дыма
    local SmokeMaterial = Material(SmokeSettings.Texture)

    ---------------------------------------------------
    -- Localized Functions
    ---------------------------------------------------
    local math_Rand = math.Rand
    local CurTime = CurTime
    local FrameTime = FrameTime
    local table_insert = table.insert
    local table_remove = table.remove
    local Lerp = Lerp
    local Vector = Vector
    local Color = Color
    local tostring = tostring
    local print = print

    ---------------------------------------------------
    -- Particle and Emitter Tables
    ---------------------------------------------------
    local SmokeParticles = {}
    local SmokeEmitters = {}

    ---------------------------------------------------
    -- Utility Functions
    ---------------------------------------------------
    -- Функция для отрисовки отладочной информации на экране
    local function DrawDebugInfo()
        local particleCount = #SmokeParticles
        local emitterCount = #SmokeEmitters
        local debugStatus = DebugMode and "ON" or "OFF"
        local text = string.format("Active Smoke Particles: %d | Emitters: %d | Debug Mode: %s",
            particleCount, emitterCount, debugStatus)
        draw.SimpleText(text, "DermaLarge", 10, 10, Color(255, 255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_TOP)
    end

    ---------------------------------------------------
    -- Environmental Parameters
    ---------------------------------------------------
    -- Здесь можно определить глобальные параметры окружающей среды.
    -- Для упрощения примера, параметры изменяются случайным образом.
    -- В реальном приложении эти параметры должны быть получены из симуляции погоды или других источников.

    local Environment = {
        WindDirection = Vector(1, 0, 0), -- Направление ветра
        WindSpeed = 10,                  -- Скорость ветра
        Humidity = 0.5,                   -- Влажность воздуха (0 - сухой, 1 - влажный)
        Temperature = 25,                -- Температура окружающей среды (Celsius)
        Stability = 0.0                   -- Атмосферная стабильность (-1: нестабильная, 0: нейтральная, 1: стабильная)
    }

    -- Функция для обновления параметров окружающей среды
    local function UpdateEnvironment()
        -- Пример: случайные изменения ветра
        Environment.WindSpeed = math_Rand(5, 15)
        local angle = math_Rand(0, 360)
        Environment.WindDirection = Vector(math.cos(math.rad(angle)), math.sin(math.rad(angle)), 0)

        -- Пример: случайные изменения влажности
        Environment.Humidity = math_Rand(0.3, 0.7)

        -- Пример: сезонные изменения температуры
        Environment.Temperature = math_Rand(15, 30)

        -- Пример: изменения атмосферной стабильности
        Environment.Stability = math_Rand(-1, 1)
    end

    ---------------------------------------------------
    -- SmokeParticle Class
    ---------------------------------------------------
    local SmokeParticle = {}
    SmokeParticle.__index = SmokeParticle

    function SmokeParticle:New(emitterPos)
        local particleSelf = setmetatable({}, SmokeParticle)
        -- Начальная позиция с распределением в пределах объёма эмиттера
        particleSelf.pos = emitterPos + Vector(
            math_Rand(SmokeSettings.EmitVolume.min.x, SmokeSettings.EmitVolume.max.x),
            math_Rand(SmokeSettings.EmitVolume.min.y, SmokeSettings.EmitVolume.max.y),
            math_Rand(SmokeSettings.EmitVolume.min.z, SmokeSettings.EmitVolume.max.z)
        )
        -- Начальная скорость с учётом случайности и турбулентности
        particleSelf.velocity = SmokeSettings.InitialVelocity + Vector(
            math_Rand(-SmokeSettings.VelocityRandomness, SmokeSettings.VelocityRandomness),
            math_Rand(-SmokeSettings.VelocityRandomness, SmokeSettings.VelocityRandomness),
            math_Rand(-10, 10)
        )
        particleSelf.size = math_Rand(SmokeSettings.ParticleSize[1], SmokeSettings.ParticleSize[2])
        particleSelf.maxParticleSize = particleSelf.size * 3
        particleSelf.colorStart = SmokeSettings.ColorStart
        particleSelf.colorEnd = SmokeSettings.ColorEnd
        particleSelf.alpha = 255
        particleSelf.spawnTime = CurTime()
        particleSelf.lifetime = math_Rand(SmokeSettings.ParticleLifetime[1], SmokeSettings.ParticleLifetime[2])
        particleSelf.collided = false
        return particleSelf
    end

    -- Обновление частицы
    function SmokeParticle:Update(dt)
        if self.collided then
            -- Опционально, обработка эффектов столкновения
            return
        end

        -- Обновление окружающей среды перед обновлением частиц
        UpdateEnvironment()

        -- Влияние плавучести с учётом температуры и стабильности атмосферы
        local buoyancyMultiplier = SmokeSettings.TemperatureEffect.BuoyancyMultiplier * (Environment.Temperature / 25)
        local stabilityFactor = 1 - math.abs(Environment.Stability) -- Чем выше стабильность, тем меньше турбулентность
        self.velocity = self.velocity + Vector(0, 0, SmokeSettings.Buoyancy * buoyancyMultiplier * dt)

        -- Влияние турбулентности с учётом атмосферной стабильности
        local turbulenceStrength = SmokeSettings.Turbulence * stabilityFactor
        local turbulence = Vector(
            math_Rand(-turbulenceStrength, turbulenceStrength),
            math_Rand(-turbulenceStrength, turbulenceStrength),
            math_Rand(-5, 5) * stabilityFactor
        )
        self.velocity = self.velocity + turbulence * dt

        -- Влияние ветра из окружающей среды
        local wind = Environment.WindDirection * Environment.WindSpeed
        self.velocity = self.velocity + wind * dt

        -- Влияние влажности на скорость распространения (более высокая влажность может замедлять подъем)
        self.velocity = self.velocity * (1 - 0.2 * Environment.Humidity)

        -- Обновление позиции
        self.pos = self.pos + self.velocity * dt

        -- Проверка столкновений с миром
        local trace = util.TraceLine({
            start = self.pos - self.velocity * dt, -- Предыдущая позиция
            endpos = self.pos,
            mask = MASK_SOLID_BRUSHONLY -- Проверка только с геометрией мира
        })

        if trace.Hit then
            self.pos = trace.HitPos
            self.velocity = Vector(0, 0, 0)
            self.collided = true
        end

        -- Расширение размера частицы для эффекта диффузии
        self.size = math.min(self.size + 15 * dt, self.maxParticleSize)

        -- Плавное затухание альфа-канала
        local age = CurTime() - self.spawnTime
        if age > (self.lifetime - SmokeSettings.FadeOutTime) then
            local fadeAlpha = 255 * (1 - (age - (self.lifetime - SmokeSettings.FadeOutTime)) / SmokeSettings.FadeOutTime)
            self.alpha = math.Clamp(fadeAlpha, 0, 255)
        end
    end

    -- Отрисовка частицы
    function SmokeParticle:Draw()
        if self.alpha <= 0 then return end

        -- Линейная интерполяция цвета от начала до конца на основе возраста
        local ageFraction = (CurTime() - self.spawnTime) / self.lifetime
        ageFraction = math.Clamp(ageFraction, 0, 1)
        local currentColor = Color(
            Lerp(ageFraction, self.colorStart.r, self.colorEnd.r),
            Lerp(ageFraction, self.colorStart.g, self.colorEnd.g),
            Lerp(ageFraction, self.colorStart.b, self.colorEnd.b),
            self.alpha * SmokeSettings.HumidityEffect.OpacityMultiplier * (1 - Environment.Humidity)
        )

        render.SetMaterial(SmokeMaterial)
        render.DrawSprite(self.pos, self.size, self.size, currentColor)
    end

    ---------------------------------------------------
    -- SmokeEmitter Class
    ---------------------------------------------------
    local SmokeEmitter = {}
    SmokeEmitter.__index = SmokeEmitter

    function SmokeEmitter:New(pos)
        local emitterSelf = setmetatable({}, SmokeEmitter)
        emitterSelf.pos = pos
        emitterSelf.lastEmitTime = 0
        return emitterSelf
    end

    -- Функция для эмиссии новых частиц из этого эмиттера
    function SmokeEmitter:Emit()
        local currentTime = CurTime()
        local emitInterval = 1 / (SmokeSettings.EmitRate * SmokeSettings.DensityMultiplier)
        if currentTime - self.lastEmitTime < emitInterval then return end

        for i = 1, SmokeSettings.EmitRate do
            if #SmokeParticles >= SmokeSettings.MaxParticles then break end
            table_insert(SmokeParticles, SmokeParticle:New(self.pos))
        end
        self.lastEmitTime = currentTime
    end

    -- Функция для отрисовки эмиттера (для отладки)
    function SmokeEmitter:Draw()
        if not DebugMode then return end
        -- Отрисовка каркаса куба, представляющего объем эмиссии
        local min = SmokeSettings.EmitVolume.min
        local max = SmokeSettings.EmitVolume.max
        render.SetColorMaterial()
        render.DrawWireframeBox(self.pos, Angle(0, 0, 0), min, max, Color(0, 255, 0, 255), true)
    end

    ---------------------------------------------------
    -- Emitter Management Functions
    ---------------------------------------------------
    -- Функция для добавления нового эмиттера
    local function AddEmitter(pos)
        local emitter = SmokeEmitter:New(pos)
        table_insert(SmokeEmitters, emitter)
        print("Smoke emitter added at:", tostring(pos))
    end

    -- Функция для удаления всех эмиттеров
    local function RemoveAllEmitters()
        SmokeEmitters = {}
        print("All smoke emitters removed.")
    end

    -- Функция для эмиссии частиц от всех эмиттеров
    local function EmitSmokeParticles()
        for _, emitter in ipairs(SmokeEmitters) do
            emitter:Emit()
        end
    end

    -- Функция для обновления и отрисовки частиц
    local function UpdateAndDrawParticles()
        local dt = FrameTime()

        -- Обновление и отрисовка частиц в обратном порядке для безопасного удаления
        for i = #SmokeParticles, 1, -1 do
            local particle = SmokeParticles[i]
            local age = CurTime() - particle.spawnTime

            if age > particle.lifetime then
                table_remove(SmokeParticles, i)
            else
                particle:Update(dt)
                particle:Draw()
            end
        end

        if DebugMode then
            -- Отрисовка всех эмиттеров
            for _, emitter in ipairs(SmokeEmitters) do
                emitter:Draw()
            end

            -- Отображение отладочной информации
            DrawDebugInfo()
        end
    end

    ---------------------------------------------------
    -- Hooks
    ---------------------------------------------------
    hook.Add("Think", "RealisticSmoke_Emit", function()
        EmitSmokeParticles()
    end)

    hook.Add("PostDrawTranslucentRenderables", "RealisticSmoke_Draw", function()
        UpdateAndDrawParticles()
    end)

    ---------------------------------------------------
    -- Console Commands
    ---------------------------------------------------
    -- Команда для добавления эмиттера дыма в заданной позиции
    -- Использование: add_smoke_emitter x y z
    concommand.Add("add_smoke_emitter", function(ply, cmd, args)
        if #args == 3 then
            local x = tonumber(args[1])
            local y = tonumber(args[2])
            local z = tonumber(args[3])

            if x and y and z then
                local pos = Vector(x, y, z)
                AddEmitter(pos)
            else
                print("Invalid coordinates. Usage: add_smoke_emitter <x> <y> <z>")
            end
        else
            print("Usage: add_smoke_emitter <x> <y> <z>")
        end
    end)

    -- Команда для удаления всех эмиттеров дыма
    -- Использование: remove_smoke_emitters
    concommand.Add("remove_smoke_emitters", function(ply, cmd, args)
        RemoveAllEmitters()
    end)

    -- Команда для переключения отладочного режима
    -- Использование: toggle_smoke_debug
    concommand.Add("toggle_smoke_debug", function(ply, cmd, args)
        DebugMode = not DebugMode
        print("Smoke Debug Mode:", DebugMode and "ON" or "OFF")
    end)

    -- Команда для установки множителя плотности дымовой завесы
    -- Использование: set_smoke_density <multiplier>
    concommand.Add("set_smoke_density", function(ply, cmd, args)
        if #args == 1 then
            local multiplier = tonumber(args[1])
            if multiplier and multiplier > 0 then
                SmokeSettings.DensityMultiplier = multiplier
                print("Smoke Density Multiplier set to:", multiplier)
            else
                print("Invalid multiplier. It must be a positive number.")
            end
        else
            print("Usage: set_smoke_density <multiplier>")
        end
    end)

    -- Команда для вывода списка всех текущих эмиттеров
    -- Использование: list_smoke_emitters
    concommand.Add("list_smoke_emitters", function(ply, cmd, args)
        print("Current Smoke Emitters:")
        if #SmokeEmitters == 0 then
            print("  None")
        else
            for i, emitter in ipairs(SmokeEmitters) do
                print(string.format("  Emitter %d: Pos = %s", i, tostring(emitter.pos)))
            end
        end
    end)

    -- Команда для настройки общих параметров дыма
    -- Использование: set_smoke_setting <setting> <value>
    concommand.Add("set_smoke_setting", function(ply, cmd, args)
        if #args < 2 then
            print("Usage: set_smoke_setting <setting> <value>")
            print("Available settings: EmitRate, MaxParticles, Spread, VelocityRandomness, FadeOutTime, DensityMultiplier")
            return
        end

        local setting = args[1]
        local value = tonumber(args[2])

        if not value then
            print("Invalid value. It must be a number.")
            return
        end

        if SmokeSettings[setting] ~= nil then
            SmokeSettings[setting] = value
            print(string.format("Smoke Setting '%s' set to %s", setting, tostring(value)))
        else
            print("Unknown setting:", setting)
            print("Available settings: EmitRate, MaxParticles, Spread, VelocityRandomness, FadeOutTime, DensityMultiplier")
        end
    end)

    -- Команда для спавна эмиттера дыма относительно позиции игрока
    -- Использование: spawn_smoke_relative <offset_x> <offset_y> <offset_z>
    concommand.Add("spawn_smoke_relative", function(ply, cmd, args)
        if not IsValid(ply) then
            print("This command can only be run by a player.")
            return
        end

        if #args == 3 then
            local offsetX = tonumber(args[1])
            local offsetY = tonumber(args[2])
            local offsetZ = tonumber(args[3])

            if offsetX and offsetY and offsetZ then
                local pos = ply:GetPos() + Vector(offsetX, offsetY, offsetZ)
                AddEmitter(pos)
            else
                print("Invalid offsets. Usage: spawn_smoke_relative <offset_x> <offset_y> <offset_z>")
            end
        else
            print("Usage: spawn_smoke_relative <offset_x> <offset_y> <offset_z>")
        end
    end)

    ---------------------------------------------------
    -- Initialization
    ---------------------------------------------------
    hook.Add("InitPostEntity", "RealisticSmoke_Initialize", function()
        -- Пример: Добавление эмиттера по умолчанию в начале
        -- Раскомментируйте строку ниже, чтобы добавить эмиттер по умолчанию
        -- AddEmitter(Vector(0, 0, 0))
    end)
end
```
Улучши и оптимизируй код, проведи код ревью и рефакторинг.