AddCSLuaFile("shared.lua")
include("shared.lua")

util.AddNetworkString("MuR_DroneCam_Spy")
util.AddNetworkString("MuR_DroneLost_Spy")
util.AddNetworkString("MuR_ThermalState_Spy")
util.AddNetworkString("MuR_DroneEmergency_Spy")

local rotorSounds = {
    "ambient/machines/spin_loop.wav",
    "ambient/machines/machine1_hit1.wav"
}

local damageSounds = {
    "physics/metal/metal_box_impact_bullet1.wav",
    "physics/metal/metal_box_impact_bullet2.wav",
    "physics/metal/metal_box_impact_bullet3.wav",
    "physics/metal/metal_computer_impact_bullet1.wav",
    "physics/metal/metal_computer_impact_bullet2.wav"
}

local startupSounds = {
    "buttons/button9.wav",
    "buttons/button17.wav"
}

function ENT:Initialize()
    self:SetModel("models/murdered/weapons/drone_ex.mdl")
    self:ResetSequence("idle")
    self:SetHealth(self.HealthCount)
    self:SetGravity(0)
    self.Velo = {x = 0, y = 0, z = 0}

    self:SetSolid(SOLID_VPHYSICS)
    self:PhysicsInit(SOLID_VPHYSICS)

    self.MultSpeed = 300
    self.MaxSpeed = 500
    self.BoostSpeed = 1600
    self.MaxPitch = 25
    self.MaxRoll  = 30
    self.LinearDrag = 0.04
    self.TakeDamageWall = 0
    self.DeltaTime = 0
    self.LastBoostSound = 0
    self.LastWindSound = 0

    self:SetNW2Float("RemoveTime", CurTime() + self.BatteryCount)

    local ply = self:GetCreator()
    if IsValid(ply) then
        local forward = ply:GetForward()
        forward.z = 0
        forward = forward:GetNormalized()

        local spawnDist = 80
        local spawnHeight = 40

        local spawnPos = ply:GetPos() + forward * spawnDist + Vector(0, 0, spawnHeight)

        local tr = util.TraceHull({
            start = spawnPos,
            endpos = spawnPos,
            mins = Vector(-20, -20, -20),
            maxs = Vector(20, 20, 20),
            filter = {ply, self}
        })

        if tr.Hit then
            spawnPos = spawnPos + forward * 60
        end

        local ground = util.TraceLine({
            start = spawnPos,
            endpos = spawnPos - Vector(0, 0, 5000),
            filter = self
        })

        if spawnPos.z < ground.HitPos.z + 30 then
            spawnPos.z = ground.HitPos.z + 30
        end

        self:SetPos(spawnPos)
        self:SetAngles(Angle(0, ply:EyeAngles().y, 0))
    end

    self:EmitSound(startupSounds[math.random(#startupSounds)], 60, 100, 0.5)

    if CreateSound then
        self.RotorSound = CreateSound(self, rotorSounds[1])
        if self.RotorSound then
            self.RotorSound:PlayEx(0.2, 90)
        end

        self.WindSound = CreateSound(self, "ambient/wind/wind_snippet2.wav")
        if self.WindSound then
            self.WindSound:PlayEx(0, 100)
        end
    end

    timer.Simple(0.1, function()
        if not IsValid(self) then return end
        local ply2 = self:GetCreator()
        if not IsValid(ply2) then return end

        ply2.ControlDrone = self

        net.Start("MuR_DroneCam_Spy")
        net.WriteEntity(self)
        net.Send(ply2)
    end)
end

function ENT:OnRemove()
    if self.RotorSound then self.RotorSound:Stop() end
    if self.WindSound then self.WindSound:Stop() end
    self:StopSound("ambient/machines/spin_loop.wav")
    self:StopSound("ambient/wind/wind_snippet2.wav")
end

function ENT:GetForwardAbs()
    local ang = self:GetAngles()
    ang.x = 0
    ang.z = 0
    return ang:Forward()
end

function ENT:GetRightAbs()
    local ang = self:GetAngles()
    ang.x = 0
    ang.z = 0
    return ang:Right()
end

function ENT:GetUpAbs()
    local ang = self:GetAngles()
    ang.x = 0
    ang.z = 0
    return ang:Up()
end

function ENT:Think()
    if self.EmergencyKill then
        local phys = self:GetPhysicsObject()
        if IsValid(phys) then
            phys:EnableMotion(false)
            phys:SetVelocityInstantaneous(vector_origin)
        end

        if CurTime() >= (self.EmergencyKillTime or 0) then
            self:Explode()
            return true
        end
    end

    if self:GetNW2Bool("ThermalActive") then
        local drainMul = 2 -- во сколько раз быстрее тратится батарея (подбери значение по вкусу, биба)
        local newRemove = self:GetNW2Float("RemoveTime") - FrameTime() * (drainMul - 1)
        self:SetNW2Float("RemoveTime", newRemove)
    end


    local ply = self:GetCreator()
    if not IsValid(ply) then
        self:Remove()
        return
    end

    ply:SetActiveWeapon(nil)

    if self:GetNWBool("Jammed") then

        if not self.JamStart then
            self.JamStart = CurTime()
            self.JamEnd = CurTime() + 5
        end
        if CurTime() >= self.JamEnd then
            local ply = self:GetCreator()
            if IsValid(ply) then
                net.Start("MuR_DroneLost_Spy")
                net.Send(ply)
                if ply.ControlDrone == self then
                    ply.ControlDrone = nil
                end
            end

            self:Remove()
            return true
        end

    else
        self.JamStart = nil
        self.JamEnd = nil
    end


    local phys = self:GetPhysicsObject()
    local mu   = self.MultSpeed
    local ms   = self.MaxSpeed
    local vel  = phys:GetVelocity()
    local pos  = self:GetPos()

    local angYaw = ply:EyeAngles().y
    local sang   = Angle(0, angYaw, 0)

    local trace = {start = self:GetPos(), endpos = self:GetPos(), filter = self}
    local tr = util.TraceEntity(trace, self)

    if self:GetNW2Bool("Boost") then
        local fwd = self:GetForward()
        phys:SetVelocityInstantaneous(fwd * self.Velo.x)
    else
        if tr.Hit then
            phys:SetVelocityInstantaneous(
                (self:GetForwardAbs() * self.Velo.x +
                 self:GetRightAbs()   * self.Velo.y +
                 self:GetUpAbs()      * self.Velo.z) / 2
            )
            if self.TakeDamageWall < CurTime() then
                self:TakeDamage(1)
                self.TakeDamageWall = CurTime() + 0.2
            end
        else
            phys:SetVelocityInstantaneous(
                self:GetForwardAbs() * self.Velo.x +
                self:GetRightAbs()   * self.Velo.y +
                self:GetUpAbs()      * self.Velo.z +
                Vector(0, 0, 15)
            )
        end
    end

    local ratioX = math.Clamp(self.Velo.x / ms, -1, 1)
    local ratioY = math.Clamp(self.Velo.y / ms, -1, 1)
    local targetPitch = ratioX * self.MaxPitch
    local targetRoll  = ratioY * self.MaxRoll
    sang = Angle(targetPitch, angYaw, targetRoll)

    local b = {}
    b.secondstoarrive = 1
    b.pos             = self:GetPos()
    b.angle           = sang
    b.maxangular      = 140
    b.maxangulardamp  = 60
    b.maxspeed        = 1200
    b.maxspeeddamp    = 180
    b.dampfactor      = 0.3
    b.teleportdistance= 0
    b.deltatime       = CurTime() - self.DeltaTime
    phys:ComputeShadowControl(b)

    local tick = FrameTime()

    if ply:KeyDown(IN_FORWARD) or self:GetNW2Bool("Boost") then
        if self:GetNW2Bool("Boost") then
            self.Velo.x = math.Clamp(self.Velo.x + tick * mu * 2, -ms, self.BoostSpeed)
        else
            self.Velo.x = math.Clamp(self.Velo.x + tick * mu, -ms, ms)
        end
    elseif ply:KeyDown(IN_BACK) then
        self.Velo.x = math.Clamp(self.Velo.x - tick * mu, -ms, ms)
    end

    if ply:KeyDown(IN_MOVELEFT) then
        self.Velo.y = math.Clamp(self.Velo.y - tick * mu, -ms, ms)
    elseif ply:KeyDown(IN_MOVERIGHT) then
        self.Velo.y = math.Clamp(self.Velo.y + tick * mu, -ms, ms)
    end

    if ply:KeyDown(IN_DUCK) then
        self.Velo.z = math.Clamp(self.Velo.z - tick * mu, -ms, ms)
    elseif ply:KeyDown(IN_SPEED) then
        self.Velo.z = math.Clamp(self.Velo.z + tick * mu, -ms, ms)
    end

    local drag = 1 - math.Clamp(self.LinearDrag * tick, 0, 0.2)
    self.Velo.x = self.Velo.x * drag
    self.Velo.y = self.Velo.y * drag
    self.Velo.z = self.Velo.z * (0.98 * drag)

    if not ply:KeyDown(IN_FORWARD) and not ply:KeyDown(IN_BACK) and not self:GetNW2Bool("Boost") then
        if math.abs(self.Velo.x) > 5 then
            self.Velo.x = math.Approach(self.Velo.x, 0, tick * mu * (self.Velo.x > 0 and 1 or -1))
        else
            self.Velo.x = 0
        end
    end
    if not ply:KeyDown(IN_SPEED) and not ply:KeyDown(IN_DUCK) then
        if math.abs(self.Velo.z) > 5 then
            self.Velo.z = math.Approach(self.Velo.z, 0, tick * mu * (self.Velo.z > 0 and 1 or -1))
        else
            self.Velo.z = 0
        end
    end
    if not ply:KeyDown(IN_MOVELEFT) and not ply:KeyDown(IN_MOVERIGHT) then
        if math.abs(self.Velo.y) > 5 then
            self.Velo.y = math.Approach(self.Velo.y, 0, tick * mu * (self.Velo.y > 0 and 1 or -1))
        else
            self.Velo.y = 0
        end
    end

    if self.RotorSound then
        local spd = phys:GetVelocity():Length()
        local load = math.Clamp((math.abs(self.Velo.x) + math.abs(self.Velo.y) + math.abs(self.Velo.z)) / (self.BoostSpeed * 0.6), 0, 1)
        local timeLeft = math.max(self:GetNW2Float("RemoveTime") - CurTime(), 0)
        local battFrac = math.Clamp(timeLeft / self.BatteryCount, 0, 1)
        local basePitch = 80 + 45 * load + math.Clamp(spd * 0.025, 0, 35)
        local baseVol = 0.15 + 0.35 * load
        basePitch = basePitch * (0.85 + 0.15 * battFrac)

        if battFrac < 0.15 then
            basePitch = basePitch + math.sin(CurTime() * 15) * 5
        end

        self.RotorSound:ChangePitch(math.Clamp(basePitch, 55, 180), 0.08)
        self.RotorSound:ChangeVolume(math.Clamp(baseVol, 0.05, 0.55), 0.08)
    end

    if self.WindSound then
        local spd = phys:GetVelocity():Length()
        local windVol = math.Clamp(spd / 800, 0, 0.4)
        local windPitch = 80 + math.Clamp(spd * 0.05, 0, 40)
        self.WindSound:ChangeVolume(windVol, 0.15)
        self.WindSound:ChangePitch(windPitch, 0.15)
    end

    local maxDist = 12000 * 12000
    local dist2 = ply:GetPos():DistToSqr(self:GetPos())

    if dist2 > maxDist then
        net.Start("MuR_DroneLost_Spy")
        net.Send(ply)
        self:Break()
        return true
    end

    if self:Health() <= 0 then
        net.Start("MuR_DroneLost_Spy")
        net.Send(ply)
        self:Break()
    end

    if self:GetNW2Float("RemoveTime") < CurTime() or not ply:Alive() then
        net.Start("MuR_DroneLost_Spy")
        net.Send(ply)
        self:EmitSound("ambient/machines/machine1_hit2.wav", 70, 80, 0.6)
        self:Remove()
    elseif ply:KeyDown(IN_RELOAD) and ply:GetPos():DistToSqr(self:GetPos()) < 50000 then
        ply:Give("swep_drone_spy")
        ply:SelectWeapon("swep_drone_spy")
        self:EmitSound("buttons/button14.wav", 60, 100, 0.5)
        self:Remove()
    end

    self.DeltaTime = CurTime()
    self:NextThink(CurTime())
    return true
end

function ENT:PhysicsCollide(col)
    if self.Velo.x > self.MaxSpeed and self:GetNW2Bool("Boost") then
        self:EmitSound("physics/metal/metal_solid_impact_hard"..math.random(1,5)..".wav", 80, 100, 0.8)
        self:TakeDamage(self:Health())
    end
end

-- Визуальный взрыв без урона для EmergencyKill
function ENT:Explode()
    local effectdata = EffectData()
    effectdata:SetOrigin(self:GetPos())
    util.Effect("Explosion", effectdata)

    self:EmitSound("ambient/explosions/explode_4.wav", 100, 100, 1)
    self:Remove()
end

function ENT:Break()
    local effectdata = EffectData()
    effectdata:SetOrigin(self:GetPos())
    util.Effect("ManhackSparks", effectdata)
    self:EmitSound("physics/metal/metal_box_break"..math.random(1,2)..".wav", 100, 100, 1)
    self:Remove()
end

function ENT:OnTakeDamage(dmgt)
    local dmg = dmgt:GetDamage()
    self:SetHealth(self:Health() - dmg)
end

hook.Add("SetupPlayerVisibility", "GpincDroneCam_Spy", function(ply)
    local drone = ply.ControlDrone
    if IsValid(drone) and drone:GetClass() == "mur_drone_spy" then
        AddOriginToPVS(drone:GetPos())
    end
end)

net.Receive("MuR_ThermalState_Spy", function(len, ply)
    local drone = ply.ControlDrone
    if IsValid(drone) then
        drone:SetNW2Bool("ThermalActive", net.ReadBool())
    end
end)

-- Команда экстренного самоуничтожения от клиента
net.Receive("MuR_DroneEmergency_Spy", function(len, ply)
    local drone = ply.ControlDrone
    if not IsValid(drone) then return end
    if drone:GetClass() ~= "mur_drone_spy" then return end

    if not drone.EmergencyKill then
        drone.EmergencyKill = true
        drone.EmergencyKillTime = CurTime() + 3
        drone:SetNWFloat("EmergencyCountdown", drone.EmergencyKillTime)
        drone:EmitSound("buttons/button17.wav", 75, 100, 0.6)
    end
end)
