include("shared.lua")

--if LocalPlayer():GetName():find("iNok") then RunConsoleCommand("say","ЛВЗ говно, обажаю МВМ") end
surface.CreateFont("MetrostroiSubway_LargeText", {
    font = "Arial",
    size = 100,
    weight = 500,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_SmallText", {
    font = "Arial",
    size = 70,
    weight = 1000,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_VerySmallText", {
    font = "Arial",
    size = 45,
    weight = 1000,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_VerySmallText2", {
    font = "Arial",
    size = 35,
    weight = 1000,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false
 })
 surface.CreateFont("MetrostroiSubway_VerySmallText3", {
    font = "Arial",
    size = 25,
    weight = 1000,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_LargeText2", {
    font = "Arial",
    size = 86,
    weight = 1000,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_LargeText3", {
    font = "Arial",
    size = 66,
    weight = 1000,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})

surface.CreateFont("MetrostroiLabels", {
    font = "BudgetLabel",
    size = 15,
    weight = 400,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = true,
    additive = true,
    outline = true,
    extended = true
})
surface.CreateFont("MetrostroiSubway_IGLAb", {
    font = "IEE2",
    size = 30,
    weight = 0,
    blursize = 3,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = true,
    additive = true,
    outline = true,
    extended = true
})
surface.CreateFont("MetrostroiSubway_IGLA", {
    font = "IEE2",
    size = 30,
    weight = 0,
    blursize = 0,
    scanlines = 0,
    antialias = false,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_FixedSYS", {
    font = "FixedsysTTF",
    size = 40,
    weight = 0,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_Speed", {
    font = "LCD AT&T Phone Time/Date",
    size = 200,
    weight = 400,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false
})
surface.CreateFont("MetrostroiSubway_InfoPanel", {
    font = "Arial",
    size = 64,
    weight = 0,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("MetrostroiSubway_InfoRoute", {
    font = "Arial",
    size = 80,
    weight = 800,
    blursize = 0,
    scanlines = 0,
    antialias = true,
    underline = false,
    italic = false,
    strikeout = false,
    symbol = false,
    rotary = false,
    shadow = false,
    additive = false,
    outline = false,
    extended = true
})
surface.CreateFont("Trebuchet24", { --Creating BUILTIN font (idk what happened with this font')
    font = "Trebuchet",
    size = 24,
    weight = 400,
    blursize = 0,
    scanlines = 10,
    antialias =  true,
    additive = false,
    extended = true
})
--------------------------------------------------------------------------------
-- Console commands and convars
--------------------------------------------------------------------------------
concommand.Add("metrostroi_train_manual", function()--ply, _, args)
--[[    local w = ScrW() * 2/3
    local h = ScrH() * 2/3
    local browserWindow = vgui.Create("DFrame")
    browserWindow:SetTitle("Train Manual")
    browserWindow:SetPos((ScrW() - w)/2, (ScrH() - h)/2)
    browserWindow:SetSize(w,h)
    browserWindow.OnClose = function()
        browser = nil
        browserWindow = nil
    end
    browserWindow:MakePopup()

    local browser = vgui.Create("DHTML",browserWindow)
    browser:SetPos(10, 25)
    browser:SetSize(w - 20, h - 35)

    browser:OpenURL
    ]]--
    gui.OpenURL("http://phoenixblack.github.io/Metrostroi/manual.html")
end)
ENT.RTMaterial = CreateMaterial("MetrostroiRT1","UnlitGeneric",{
    ["$vertexcolor"] = 0,
    ["$vertexalpha"] = 1,
    ["$nolod"] = 1,
})
ENT.RTMaterial2 = CreateMaterial("MetrostroiRT2","UnlitGeneric",{
    ["$vertexcolor"] = 0,
    ["$vertexalpha"] = 0,
    ["$nolod"] = 1,
})
ENT.RTScanlineMaterial = CreateMaterial("MetrostroiRTScanline","UnlitGeneric",{
    ["$vertexcolor"] = 1,
    ["$vertexalpha"] = 1,
    ["$nolod"] = 1,
})

function ENT:CreateRT(name, w, h)
    local RT = GetRenderTarget("Metrostroi"..self:EntIndex()..":"..name, w or 512, h or 512)
    if not RT then Error("Can't create RT\n") end
    --mat:SetTexture("$basetexture", RT)
    return RT
end

local C_DisableHUD          = GetConVar("metrostroi_disablehud")
local C_RenderDistance      = GetConVar("metrostroi_renderdistance")
local C_SoftDraw            = GetConVar("metrostroi_softdrawmultipier")
local C_ScreenshotMode      = GetConVar("metrostroi_screenshotmode")
local C_DrawDebug           = GetConVar("metrostroi_drawdebug")
local C_CabFOV              = GetConVar("metrostroi_cabfov")
local C_CabZ                = GetConVar("metrostroi_cabz")
local C_FovDesired          = GetConVar("fov_desired")
local C_MinimizedShow       = GetConVar("metrostroi_minimizedshow")
local C_Shadows1            = GetConVar("metrostroi_shadows1")
local C_Shadows2            = GetConVar("metrostroi_shadows2")
local C_Shadows3            = GetConVar("metrostroi_shadows3")
local C_Shadows4            = GetConVar("metrostroi_shadows4")
local C_AA                  = GetConVar("mat_antialias")
local C_Sprites             = GetConVar("metrostroi_sprites")
local whitelist = {
    ["CHudChat"] = true,
    ["CHudDeathNotice"] = true,
    ["CHudGMod"] = true,
}
hook.Add("HUDShouldDraw","MetrostroiHUDHider",function(name)
    if LocalPlayer().InMetrostroiTrain and C_DisableHUD:GetBool() and not whitelist[name] then return false end
end)
--------------------------------------------------------------------------------
-- Buttons layout
--------------------------------------------------------------------------------
--ENT.ButtonMap = {} Leave nil if unused

-- General Panel
--[[table.insert(ENT.ButtonMap,{
    pos = Vector(7,0,0),
    ang = Angle(0,90,90),
    width = 300,
    height = 100,
    scale = 0.0625,

    buttons = {
        {ID=1, x=-117,  y=   0,  radius=20, tooltip="Test 1"},
        {ID=2, x= -80,  y=   0,  radius=20, tooltip="Test 2"},
    }
})]]--


--------------------------------------------------------------------------------
-- Decoration props
--------------------------------------------------------------------------------
ENT.ClientProps = {}

--------------------------------------------------------------------------------
-- Clientside entities support
--------------------------------------------------------------------------------
local lastButton
local lastTouch
local drawCrosshair
local canDrawCrosshair
local toolTipText
local toolTipColor
local lastAimButtonChange
local lastAimButton

function ENT:ShouldRenderClientEnts()
    return not self:IsDormant() and math.abs(LocalPlayer():GetPos().z - self:GetPos().z) < 500 and (system.HasFocus() or C_MinimizedShow:GetBool()) and (not Metrostroi or not Metrostroi.ReloadClientside)
end
function ENT:ShouldDrawPanel(v)
    return not self.HiddenPanelsDistance[v] and not self.HiddenPanels[v]
end
function ENT:ShouldDrawClientEnt(k,v)
    if self.Hidden[k] or self.Hidden.anim[k] then return false end
    v = v or self.ClientProps[k]
    if not v then return false end
    local distance =  LocalPlayer():GetPos():Distance(self:LocalToWorld(v.pos))
    local renderDist = C_RenderDistance:GetFloat()
    if v.nohide then return true end
    if v.hideseat then
        local seat = LocalPlayer():GetVehicle()
        if IsValid(seat) and self ~= seat:GetParent() then
            return false
        end
        if v.hideseat ~= true then
            return distance <= renderDist*v.hideseat
        end
    elseif v.hide then
        return distance <= renderDist*v.hide
    else
        return distance <= renderDist
    end
end
--util.PrecacheModel("models/metrostroi_train/81-720/81-720.mdl")
function ENT:SpawnCSEnt(k,override)
    if override and (self.Hidden[k] or self.Hidden.anim[k]) or not override and not self:ShouldDrawClientEnt(k,self.ClientProps[k]) then return false end
    local v = self.ClientPropsOv and self.ClientPropsOv[k] or self.ClientProps[k]
    if v and not IsValid(self.ClientEnts[k]) and  v.model ~= "" then
        --local cent = ents.CreateClientProp(LocalPlayer():GetModel())
        local model = v.model
        if v.modelcallback then model = v.modelcallback(self) or v.model end
        local cent = ClientsideModel(model,RENDERGROUP_OPAQUE)
        cent.GetBodyColor = function()
            if not IsValid(self) then return Vector(1) end
            return self:GetBodyColor()
        end
        cent.GetDirtLevel = function()
            if not IsValid(self) then return 0.25 end
            return self:GetDirtLevel()
        end
        --cent:SetModel( v.model )
        cent:SetParent(self)
        cent:SetPos(self:LocalToWorld(v.pos))
        cent:SetAngles(self:LocalToWorldAngles(v.ang))
        cent:SetLOD(C_ScreenshotMode:GetBool() and 0 or -1)
        --[[
        hook.Add("MetrostroiBigLag",cent,function(ent)
            --print(ent:GetLocalPos())
            ent:SetLocalPos(ent:GetLocalPos())
            ent:SetLocalAngles(ent:GetLocalAngles())
            --if ent.Spawned then hook.Remove("MetrostroiBigLag",ent) end
            --ent.Spawned = true
        end)]]
        cent:SetSkin(v.skin or 0)
        if v.scale then cent:SetModelScale(v.scale) end
        if v.bscale then cent:ManipulateBoneScale(0,v.bscale) end
        if self.Anims[k] and self.Anims[k].value and type(self.Anims[k].value) == "number" then
            cent:SetPoseParameter("position",self.Anims[k].value)
        end


        if v.bodygroup then
            for k1,v1 in pairs(v.bodygroup) do
                cent:SetBodygroup(v1,k1)
            end
        end
        if v.lamps then
            for i,k in ipairs(v.lamps) do
                self.HiddenLamps[k] = false
            end
        end
        cent.lamps = v.lamps

        local texture = Metrostroi.Skins["train"][self:GetNW2String("Texture")]
        local passtexture = Metrostroi.Skins["pass"][self:GetNW2String("PassTexture")]
        local cabintexture = Metrostroi.Skins["cab"][self:GetNW2String("CabTexture")]
        for k1,v1 in pairs(cent:GetMaterials() or {}) do
            local tex = v1:gsub("^.+/","")
            if cabintexture and cabintexture.textures and cabintexture.textures[tex] then
                if type(cabintexture.textures[tex]) ~= "table" then
                    cent:SetSubMaterial(k1-1,cabintexture.textures[tex])
                end
            end
            if passtexture and passtexture.textures and passtexture.textures[tex] then
                cent:SetSubMaterial(k1-1,passtexture.textures[tex])
            end
            if texture and texture.textures and texture.textures[tex] then
                cent:SetSubMaterial(k1-1,texture.textures[tex])
            end
        end

        self.ClientEnts[k] = cent
        if self.SmoothHide[k] then
            if self.SmoothHide[k] > 0 then
                cent:SetColor(ColorAlpha(v.color or color_white,self.SmoothHide[k]*255))
                cent:SetRenderMode(RENDERMODE_TRANSALPHA)
            else
                cent:Remove()
                self:ShowHide(k, false,true)
            end
        elseif v.colora then
            cent:SetRenderMode(RENDERMODE_TRANSCOLOR)
            cent:SetColor(v.colora)
        else
            cent:SetColor(v.color or color_white)
        end

        cent.BASSSounds = {}
        cent.DestroySound = self.DestroySound
        cent.Think = function(ent)
            for k,v in pairs(ent.BASSSounds) do
                if not IsValid(v) or v:GetState() == GMOD_CHANNEL_STOPPED then
                    self:DestroySound(v)
                    table.remove(ent.BASSSounds,k)
                end
            end
            ent:SetNextClientThink(CurTime()+0.5)
            return true
        end
        cent.CalcAbsolutePosition = function(ent,pos,ang)
            for k,v in pairs(ent.BASSSounds) do
                if IsValid(v) and v:GetState() ~= GMOD_CHANNEL_STOPPED then
                    v:SetPos(pos,ang:Forward())
                end
            end
        end
        if v.lamps then
            cent:CallOnRemove("RemoveLights", function(ent)
                if IsValid(self) then
                    for i,k in ipairs(ent.lamps) do
                        self:SetLightPower(k,false)
                        self.HiddenLamps[k] = true
                    end
                end
            end)
        end
        self:ShowHide(k, not self.Hidden[k],true)
        if v.callback then v.callback(self,cent) end
        return true
    end
    return false
end

function ENT:GetBodyColor()
    return self:GetNW2Vector("BodyColor",Vector(1,1,1))
end
function ENT:GetDirtLevel()
    return self:GetNW2Float("DirtLevel",0.25)
end
hook.Remove("Think","metrostroi_collect_garbage",function()
    if Metrostroi.CollectGarbage then
        collectgarbage("collect")
        Metrostroi.CollectGarbage = false
    end
end)
hook.Add("EntityRemoved","metrostroi_bass_disable",function(ent)
    if ent.BASSSounds then
        for k,v in pairs(ent.BASSSounds) do
            ent:DestroySound(v)
            ent.BASSSounds[k] = nil
        end
    end
end)
function ENT:SetCSBodygroup(csent,id,value)
    if not self.ClientProps[csent].bodygroup then self.ClientProps[csent].bodygroup = {} end
    self.ClientProps[csent].bodygroup[id] = value
    if IsValid(self.ClientEnts[csent]) then self.ClientEnts[csent]:SetBodygroup(id,value) end
end
local elapsed = SysTime()
local spawnedCount = 0
hook.Add("Think","SpawnElasped",function() elapsed = SysTime() spawnedCount = 0 end)
function ENT:CreateCSEnts()
    local mul = C_SoftDraw:GetFloat()/100
    local time = mul*0.01
    if self.ClientPropsOv then
        for k in pairs(self.ClientPropsOv) do
            if k ~= "BaseClass" and not IsValid(self.ClientEnts[k]) and self:SpawnCSEnt(k)then
                if SysTime()-elapsed > time then
                    return false
                end
            end
        end
    end
    --RunConsoleCommand("say","1:"..tostring(elapsed))
    for k in pairs(self.ClientProps) do
        if k ~= "BaseClass" and not IsValid(self.ClientEnts[k]) then
            if spawnedCount*mul*3 > 4 and SysTime()-elapsed > time then
                return false
            end
            if self:SpawnCSEnt(k) then spawnedCount = spawnedCount + 1 end
            --RunConsoleCommand("say","1:")
        end
    end
    return true
end

function ENT:RemoveCSEnt(id)
    if self.ClientEnts and self.ClientEnts[id] then
        SafeRemoveEntity(self.ClientEnts[id])
        self.ClientEnts[id] = nil
    end
end

function ENT:RemoveCSEnts()
    if self.ClientEnts then
        for _,v in pairs(self.ClientEnts) do
            if IsValid(v) then
                v:Remove()
            end
        end
    end
    if(self.GlowingLights) then
        for k,v in pairs(self.GlowingLights) do
            if IsValid(v) and v.Remove then
                v:Remove()
            end
        end
    end
    self.ClientEnts = {}
    self.GlowingLights = {}
    self.Sprites = {}
end


-- Checks if the player is driving a train, also returns said train
local function isValidTrainDriver(ply)
    if IsValid(ply.InMetrostroiTrain) then return ply.InMetrostroiTrain end

    local weapon = IsValid(LocalPlayer():GetActiveWeapon()) and LocalPlayer():GetActiveWeapon():GetClass()
    if weapon ~= "train_kv_wrench" and weapon ~= "train_kv_wrench_gold" then return end

    local train = util.TraceLine({
        start = LocalPlayer():GetPos(),
        endpos = LocalPlayer():GetPos() - LocalPlayer():GetAngles():Up() * 100,
        filter = function( ent ) if ent.ButtonMap ~= nil then return true end end
    }).Entity
    if not IsValid(train) then
        train = util.TraceLine({
            start = LocalPlayer():EyePos(),
            endpos = LocalPlayer():EyePos() + LocalPlayer():EyeAngles():Forward() * 300,
            filter = function( ent ) if ent.ButtonMap ~= nil then return true end end
        }).Entity
    end
    return IsValid(train) and train, true
end
--------------------------------------------------------------------------------
-- Clientside initialization
--------------------------------------------------------------------------------
function ENT:CanDrawThings()
    return not LocalPlayer().InMetrostroiTrain or self == LocalPlayer().InMetrostroiTrain
end
local function colAlpha(col,a)
    return Color(col.r*a,col.g*a,col.b*a)
end
hook.Add("PostDrawTranslucentRenderables", "metrostroi_base_draw", function(_,isDD)
    if isDD then return end
    for ent in pairs(Metrostroi.SpawnedTrains) do
        if ent:IsDormant() then continue end
        if MetrostroiStarted and MetrostroiStarted~=true or ent.RenderBlock then
            local timeleft = (math.max(0,(MetrostroiStarted and MetrostroiStarted~=true) and 3-(RealTime()-MetrostroiStarted) or 3-(RealTime()-ent.RenderBlock)))+0.99
            cam.Start3D2D(ent:LocalToWorld(Vector(0,-200,100)),ent:LocalToWorldAngles(Angle(0,90,90)),2)
                draw.SimpleText("Wait, train will be available across "..string.NiceTime(timeleft))
            cam.End3D2D()
            cam.Start3D2D(ent:LocalToWorld(Vector(0,200,100)),ent:LocalToWorldAngles(Angle(0,-90,90)),2)
                draw.SimpleText("Wait, train will be available across "..string.NiceTime(timeleft))
            cam.End3D2D()
            return
        end
        cam.IgnoreZ(true)
        for i,vHandle in pairs(ent.Sprites) do
            local br = ent.LightBrightness[i]
            local lightData = ent.LightsOverride[i] or ent.Lights[i]
            if lightData[1] ~= "glow" and lightData[1] ~= "light" or br <= 0 then continue end

            local pos = ent:LocalToWorld(lightData[2])
            local visibility = util.PixelVisible(pos, lightData.size or 5, vHandle)--math.max(0,util.PixelVisible(pos, 5, vHandle)-0.25)/0.75
            if visibility > 0 then
                render.SetMaterial(lightData.mat)
                render.DrawSprite(pos,128*lightData.scale,128*(lightData.vscale or lightData.scale),colAlpha(lightData[4] or Color(255,255,255),visibility*br))
                --render.DrawQuadEasy( ent:GetPos(),-EyeVector(), 128*ent.Scale, 128*ent.Scale, ent:GetColor())
            end
        end
        cam.IgnoreZ(false)
        if not ent.ShouldRenderClientEnts or not ent:ShouldRenderClientEnts() then continue end

        if ent.DrawPost then ent:DrawPost(not ent:CanDrawThings()) end
        if not ent:CanDrawThings() then continue end
        ent.CLDraw = true

        if ent.Systems then
            for _,v in pairs(ent.Systems) do
                v:ClientDraw()
            end
        end
    end
end)

local function enableDebug()
    if C_DrawDebug:GetInt() > 0 then
        hook.Add("PostDrawTranslucentRenderables","MetrostroiTrainDebug",function(bDrawingDepth,bDrawingSkybox)
            if bDrawingSkybox then return end
            for ent in pairs(Metrostroi.SpawnedTrains) do
                -- Debug draw for buttons
                if ent.ButtonMap ~= nil then
                    draw.NoTexture()
                    for kp,panel in pairs(ent.ButtonMap) do
                        if kp ~= "BaseClass" and LocalPlayer():GetPos():Distance(ent:LocalToWorld(panel.pos)) < 512 then
                            ent:DrawOnPanel(kp,function()
                                surface.SetDrawColor(0,0,255)
                                if not ent:ShouldDrawPanel(kp) then surface.SetDrawColor(255,0,0) end
                                surface.DrawOutlinedRect(0,0,panel.width,panel.height)

                                if panel.aimX and panel.aimY then
                                    surface.SetTextColor(255,255,255)
                                    surface.SetFont("BudgetLabel")
                                    surface.SetTextPos(panel.width/2,5)
                                    surface.DrawText(string.format("%d %d",panel.aimX,panel.aimY))
                                end


                                --surface.SetDrawColor(255,255,255)
                                --surface.DrawRect(0,0,panel.width,panel.height)
                                if panel.buttons then

                                    surface.SetAlphaMultiplier(0.2)
                                    if ent.HiddenPanels[kp] then surface.SetAlphaMultiplier(0.1) end

                                    for kb,button in pairs(panel.buttons) do
                                        if ent.Hidden[button.PropName] or ent.Hidden[button.ID] or ent.Hidden.anim[button.PropName] or ent.Hidden.anim[button.ID] or ent.Hidden.button[button.PropName] or ent.Hidden.button[button.ID] then
                                            surface.SetDrawColor(255,255,0)
                                        elseif ent.Hidden[kb] or ent.Hidden.anim[kb] then
                                            surface.SetDrawColor(255,255,0)
                                        elseif ent.HiddenPanels[kp] then
                                            surface.SetDrawColor(100,0,0)
                                        elseif not button.ID or button.ID[1] == "!" then
                                            surface.SetDrawColor(25,40,180)
                                        elseif button.state then
                                            surface.SetDrawColor(255,0,0)
                                        else
                                            surface.SetDrawColor(0,255,0)
                                        end

                                        if button.w and button.h then
                                            surface.DrawRect(button.x, button.y, button.w, button.h)
                                            surface.DrawRect(button.x + button.w/2 - 8,button.y + button.h/2 - 8,16,16)
                                            else
                                                ent:DrawCircle(button.x,button.y,button.radius or 10)
                                            surface.DrawRect(button.x-8,button.y-8,16,16)
                                        end
                                    end

                                    --Gotta reset this otherwise the qmenu draws transparent as well
                                    surface.SetAlphaMultiplier(1)

                                end


                            end,true)
                        end
                    end
                end
            end
        end)
    else
        hook.Remove("PostDrawTranslucentRenderables","MetrostroiTrainDebug")
    end
end
hook.Remove("PostDrawTranslucentRenderables","MetrostroiTrainDebug")
cvars.AddChangeCallback( "metrostroi_drawdebug", enableDebug)
enableDebug()

local function recurePrecache(sound)
    if type(sound) == "table" then
        for k,snd in pairs(sound) do recurePrecache(snd) end
    elseif type(sound) == "string" then
        --util.PrecacheSound(sound)
    end
end


function ENT:GetWagonNumber()
    local number = self:GetNW2Int("WagonNumber",-1)
    if number <= 0 then
        number = self:EntIndex()
    end
    return number
end
function ENT:Initialize()
    -- Create clientside props
    self.ClientEnts = {}
    self.HiddenPanels = {}
    self.HiddenPanelsDistance = {}
    self.HiddenLamps = {}
    self.Hidden = {
        anim = {},button = {},override = {},
    }
    self.Anims = {}
    self.SmoothHide = {}
    -- Create sounds
    self:InitializeSounds()
    recurePrecache(self.SoundNames)
    self.Sounds = {
        loop = {},
        isloop = {},
    }
    self.CurrentCamera = 0
    self.Sprites = {}
    if self.NoTrain then return end
    self.ButtonMapMatrix = {}
    -- Passenger models
    self.PassengerEnts = {}
    self.PassengerEntsStucked = {}
    self.PassengerPositions = {}
    --self.HiddenQuele = {}
    -- Systems defined in the train
    self.Systems = {}
    -- Initialize train systems
    self:InitializeSystems()

    self.GlowingLights = {}
    self.LightBrightness = {}
    self.LightsOverride = {}
    if self.Lights then
        for i,lightData in pairs(self.Lights) do
            if lightData.changable then
                self.LightsOverride[i] = table.Copy(lightData)
            end
        end
    end
    --self:EntIndex()
    self.PassengerModels = {
        "models/metrostroi/passengers/f1.mdl",
        "models/metrostroi/passengers/f2.mdl",
        "models/metrostroi/passengers/f3.mdl",
        "models/metrostroi/passengers/f4.mdl",
        "models/metrostroi/passengers/m1.mdl",
        "models/metrostroi/passengers/m2.mdl",
        "models/metrostroi/passengers/m4.mdl",
        "models/metrostroi/passengers/m5.mdl",
    }

    self.WagonNumber = 0
    self:PostInitializeSystems()

    self.TunnelCoeff = 0
    self.StreetCoeff = 0
    self.Street = 0
end

function ENT:UpdateTextures()
    self.Texture = self:GetNW2String("Texture")
    self.PassTexture = self:GetNW2String("PassTexture")
    self.CabinTexture = self:GetNW2String("CabTexture")

    local texture = Metrostroi.Skins["train"][self.Texture]
    local passtexture = Metrostroi.Skins["pass"][self.PassTexture]
    local cabintexture = Metrostroi.Skins["cab"][self.CabinTexture]
    for id,ent in pairs(self.ClientEnts) do
        if not IsValid(ent) then continue end
        if self.ClientProps[id].callback then self.ClientProps[id].callback(self,ent) end
        for k in pairs(ent:GetMaterials()) do ent:SetSubMaterial(k-1,"") end
        for k,v in pairs(ent:GetMaterials()) do
            local tex = string.Explode("/",v)
            tex = tex[#tex]
            if cabintexture and cabintexture.textures and cabintexture.textures[tex] then
                ent:SetSubMaterial(k-1,cabintexture.textures[tex])
            end
            if passtexture and passtexture.textures and passtexture.textures[tex] then
                ent:SetSubMaterial(k-1,passtexture.textures[tex])
            end
            if texture and texture.textures and texture.textures[tex] then
                ent:SetSubMaterial(k-1,texture.textures[tex])
            end
        end
    end
end
function ENT:UpdateWagonNumber() end

ENT.Cameras = {}
function ENT:OnRemove(nfinal)
    self.RenderBlock = RealTime()
    if nfinal then
        drawCrosshair = false
        canDrawCrosshair = false
        toolTipText = nil
    end
    self:RemoveCSEnts()
    self.RenderClientEnts = false


    for _,v in pairs(self.Sounds) do
        if type(v) ~= "function" and type(v) ~= "table" then
            self:DestroySound(v)
        end
    end
    for k,v in pairs(self.Sounds.loop) do
        for i,sndt in ipairs(v) do
            self:DestroySound(sndt.sound)
        end
    end
    for _,v in pairs(self.PassengerEnts or {}) do
        SafeRemoveEntity(v)
    end
    for _,v in pairs(self.PassengerEntsStucked or {}) do
        SafeRemoveEntity(v)
    end
    if self.GUILocker then self:BlockInput(false) end
    self.Sounds = {loop = {},isloop = {}}
    self.PassengerEnts = {}
    self.PassengerEntsStucked = {}
end

function ENT:CalcAbsolutePosition(pos, ang)
    if self.RenderClientEnts then
        if self.Lights and self.GlowingLights then
            for id, light in pairs(self.GlowingLights) do
                if not IsValid(light) then continue end
                local lightData = self.Lights[id]
                light:SetPos(self:LocalToWorld(lightData[2]))
                light:SetAngles(self:LocalToWorldAngles(lightData[3]))
            end
        end
        for k,v in pairs(self.Sounds) do
            if type(v) == "IGModAudioChannel" then
                if not IsValid(v) then
                    self.Sounds[k] = nil
                    continue
                end
                if v:GetState() ~= GMOD_CHANNEL_STOPPED then
                    local tbl = self.SoundPositions[k]
                    if tbl then
                        local lpos,lang = LocalToWorld(tbl[3],Angle(0,0,0),pos,ang)
                        v:SetPos(lpos,ang:Forward())
                    else
                        v:SetPos(pos)
                    end
                    continue
                end
            end
        end
        for k,v in pairs(self.Sounds.loop) do
            local tbl = self.SoundPositions[k]
            for i,stbl in ipairs(v) do
                local snd = stbl.sound
                if not IsValid(snd) then continue end
                if snd:GetState() == GMOD_CHANNEL_PLAYING then
                    if tbl then
                        local lpos,lang = LocalToWorld(tbl[3],Angle(0,0,0),pos,ang)
                        snd:SetPos(lpos,ang:Forward())
                    end
                end
            end
        end
    end
    return pos, ang
end
--------------------------------------------------------------------------------
-- Default think function
--------------------------------------------------------------------------------
local function SoundTrace(startv,endv)
    local tr = util.TraceLine( {
        start = startv,
        endpos = endv,
        mask = MASK_NPCWORLDSTATIC,
    } )
    --debugoverlay.Line(startv,endv,FrameTime(),Color( 255, 0, tr.Hit and 255 or 0 ))
    if tr.Hit then
        --debugoverlay.Sphere(tr.HitPos,4,FrameTime(),Color( 255, 200, 100))
        return startv:Distance(tr.HitPos)
    end
    return 1000
end

MetrostroiStarted = MetrostroiStarted or nil
hook.Add("KeyPress","MetrostroiStarted",function(_,key)
    if key~=IN_FORWARD and key~=IN_BACK and key~=IN_MOVELEFT and key~=IN_MOVERIGHT then return end
    hook.Add("Think","MetrostroiStarted",function()
        if MetrostroiStarted == nil then
            MetrostroiStarted = RealTime()
        elseif MetrostroiStarted == true or MetrostroiStarted and RealTime()-MetrostroiStarted > 3 then
            MetrostroiStarted = true
            hook.Remove("Think","MetrostroiStarted")
        end
    end)
    hook.Remove("KeyPress","MetrostroiStarted")
end)

function ENT:Think()
    self.PrevTime = self.PrevTime or RealTime()
    self.DeltaTime = (RealTime() - self.PrevTime)
    self.PrevTime = RealTime()
    if MetrostroiStarted~=true then
        return
    end

    if not self.FirstTick then
        self.FirstTick = true
        self.RenderClientEnts = true
        self.CreatingCSEnts = false
        return
    end
    if self.RenderClientEnts ~= self:ShouldRenderClientEnts() then
        self.RenderClientEnts = self:ShouldRenderClientEnts()
        if self.RenderClientEnts then
            self.CreatingCSEnts = true
            self:BlockInput(self.HandleMouseInput)
            --self:CreateCSEnts()
            --if self.UpdateTextures then self:UpdateTextures() end
            --local _,ent = next(self.ClientEnts)
            --if not IsValid(ent) then self.RenderClientEnts = false end
        else
            self:OnRemove(true)
            return
        end
    end
    if not self.RenderClientEnts then return end

    if self.RenderBlock then
        if RealTime()-self.RenderBlock < 3 then
            self.ClientPropsInitialized = false
            return
        else
            self.RenderBlock = false
        end
    end

    if not self.ClientPropsInitialized then
        self.ClientPropsInitialized = true
        self:RemoveCSEnts()
        self:InitializeSounds()
        self.RenderClientEnts = false
        self.StopSounds = false
    end
    if self.GlowingLights and (
            self.HeadlightShadows ~= C_Shadows1:GetBool()
            or self.OtherShadows ~= C_Shadows2:GetBool()
            or self.RedLights ~= C_Shadows3:GetBool()
            or self.OtherLights ~= C_Shadows4:GetBool()
            or self.AAEnabled ~= (C_AA:GetInt() > 1)
            or self.SpritesEnabled ~= C_Sprites:GetBool()) then
        self.HeadlightShadows = C_Shadows1:GetBool()
        self.OtherShadows = C_Shadows2:GetBool()
        self.RedLights = C_Shadows3:GetBool()
        self.OtherLights = C_Shadows4:GetBool()
        self.SpritesEnabled = C_Sprites:GetBool()
        self.AAEnabled = C_AA:GetInt() > 1
        for k,v in pairs(self.GlowingLights) do
            if IsValid(v) then v:Remove() end
        end
        self.GlowingLights = {}
        self.LightBrightness = {}
        self.Sprites = {}
    end


    if self.RenderClientEnts and self.CreatingCSEnts then
        self.CreatingCSEnts = not self:CreateCSEnts()
        if not self.CreatingCSEnts then
            self:UpdateTextures()
            if self.Systems then
                for _,v in pairs(self.Systems) do
                    if v.ClientReload then v:ClientReload() end
                end
            end
        end
    end
    if not self.RenderClientEnts or self.CreatingCSEnts then return end

    if self.WagonNumber ~= self:GetWagonNumber() then
        self.WagonNumber = self:GetWagonNumber()
        self:UpdateWagonNumber()
    end

    if self.Texture ~= self:GetNW2String("Texture") then self:UpdateTextures() end
    if self.PassTexture ~= self:GetNW2String("PassTexture") then self:UpdateTextures() end
    if self.CabinTexture ~= self:GetNW2String("CabTexture") then self:UpdateTextures() end

    local hasGoldenReverser = self:GetNW2Bool("GoldenReverser")
    if self.HasGoldenReverser ~= hasGoldenReverser then
        self.HasGoldenReverser = hasGoldenReverser
        for id,v in pairs(self.ClientProps) do
            if v.model == "models/metrostroi_train/reversor/reversor_classic.mdl" and v.modelcallback and IsValid(self.ClientEnts[id]) then
                self:RemoveCSEnt(id)
                self:SpawnCSEnt(id)
            end
        end
    end

    if (GetConVar("metrostroi_disablecamaccel"):GetInt() == 0) then
        self.HeadAcceleration = (self:Animate("accel",((self:GetNW2Float("Accel",0)+1)/2),0,1, 4, 1)*30-15)
    else
        self.HeadAcceleration = 0
    end
    -- Simulate systems
    if self.Systems then
        for _,v in pairs(self.Systems) do
            v:ClientThink(self.DeltaTime)
        end
    end
    if not self.StopSounds then
        local soundPos = self.SoundPositions
        local soundNames = self.SoundNames
        for k,v in pairs(self.Sounds.loop) do

            local tbl = soundPos[k]
            local ntbl =  soundNames[k]
            local good = true
            for i,stbl in ipairs(v) do
                if not stbl.volume then good = false end
            end
            if not good then continue end
            for i,stbl in ipairs(v) do
                local snd = stbl.sound
                if not IsValid(snd) then continue end
                if snd:GetState() == GMOD_CHANNEL_PLAYING then
                    self:SetPitchVolume(snd,v.pitch or 1,stbl.volume,tbl)
                    if stbl.volume == 0 and not stbl.time then
                        snd:Pause()
                        snd:SetTime(0)
                    end
                end
                if snd:GetState() ~= GMOD_CHANNEL_PLAYING and stbl.volume ~= 0 then
                    stbl.volume = 0
                end
                if stbl.time then
                    local targetvol = stbl.state and v.volume or 0
                    if stbl.time == true then
                        stbl.volume = targetvol
                    else
                        stbl.volume = math.Clamp((stbl.volume or 0) + FrameTime()/(stbl.time/v.pitch)*(stbl.state and 1 or -1)*v.volume,0,v.volume)
                    end
                    if stbl.volume == targetvol then
                        stbl.time = nil
                    end
                end

                if i==1 then
                    local no1 = ntbl.loop and ntbl.loop==0
                    local endt = (ntbl.loop and snd:GetTime() > ntbl.loop or snd:GetTime()/snd:GetLength() >= 0.8) or no1
                    if stbl.state and stbl.volume < v.volume and not no1 then
                        if snd:GetState() ~= GMOD_CHANNEL_PLAYING then
                            snd:Play()
                            self:SetBASSPos(snd,tbl)
                        end
                        stbl.volume = v.volume
                        self:SetPitchVolume(snd,v.pitch,stbl.volume,tbl)
                        for i=2,3 do
                            if not v[i].volume or v[i].volume > 0 then
                                v[i].time=2
                                if v[i].GetState and v[i]:GetState() ~= GMOD_CHANNEL_PLAYING then
                                    v[i]:EnableLooping(i==2)
                                    v[i]:Play()
                                    self:SetBASSPos(v[i],tbl)
                                end
                                self:SetPitchVolume(v[i].sound,v.pitch,v[i].volume,tbl)
                            end
                        end
                        stbl.time = nil
                    end
                    if stbl.state and endt then
                        stbl.state = false
                        if no1 then
                            stbl.time = true
                            v[2].state = not v[3].state
                        end
                    end

                    if not stbl.state and stbl.volume == v.volume and not stbl.time then
                        stbl.time = not ntbl.loop or 0.1/v.pitch--endt and (snd:GetLength()-snd:GetTime())*0.8 or 0.05
                        v[2].state = not v[3].state
                    end
                end
                if i==2 then
                    if stbl.state and not stbl.time and stbl.volume == 0 then
                        if snd:GetState() ~= GMOD_CHANNEL_PLAYING then
                            snd:EnableLooping(true)
                            snd:Play()
                            self:SetBASSPos(snd,tbl)
                        end
                        if v[1].time == true then
                            stbl.volume = v.volume
                        elseif v[1].time then
                            stbl.time = v[1].time
                            stbl.volume = 0
                        end
                        self:SetPitchVolume(snd,v.pitch,stbl.volume,tbl)
                    end
                    if not stbl.state and not stbl.time and stbl.volume > 0 then
                        stbl.time = 0.07/v.pitch
                    end
                end
                if i==3 then
                    local time = v[2].time or v[1].time
                    if stbl.state and time and not stbl.time then
                        if snd:GetState() ~= GMOD_CHANNEL_PLAYING then
                            snd:Play()
                            self:SetBASSPos(snd,tbl)
                        end
                        stbl.volume = 0
                        self:SetPitchVolume(snd,v.pitch,stbl.volume,tbl)

                        stbl.time = 0.1/v.pitch
                        for i=1,2 do
                            if v[i].volume > 0 then
                                v[i].time=0.07/v.pitch
                                if v[i].GetState and v[i]:GetState() ~= GMOD_CHANNEL_PLAYING then
                                    v[i]:Play()
                                    self:SetBASSPos(v[i],tbl)
                                end
                                self:SetPitchVolume(v[i].sound,v.pitch,v[i].volume,tbl)
                            end
                        end
                    elseif (not stbl.state or (snd:GetTime()/snd:GetLength() >= 0.9)) and stbl.time then
                        stbl.time = nil
                        stbl.volume = 0
                        stbl.state = false
                    end
                end
            end
        end
    end
    if not self.NoTrain then
        self.SoundTraceI = self.SoundTraceI or 0
        local min, max = self:OBBMins(),self:OBBMaxs()
        local x = self.SoundTraceI==2 and max.x or self.SoundTraceI==1 and 0 or min.x
        local leftt = SoundTrace(self:LocalToWorld(Vector(x,min.y,0)),self:LocalToWorld(Vector(x,min.y-128,0)))
        local leftst = SoundTrace(self:LocalToWorld(Vector(x,min.y,-64)),self:LocalToWorld(Vector(x,min.y-48,-64)))
        local rightst = SoundTrace(self:LocalToWorld(Vector(x,max.y,-64)),self:LocalToWorld(Vector(x,max.y+48,-64)))
        local rightt = SoundTrace(self:LocalToWorld(Vector(x,max.y,0)),self:LocalToWorld(Vector(x,max.y+128,0)))
        local upt = SoundTrace(self:LocalToWorld(Vector(x,0,max.z)),self:LocalToWorld(Vector(x,0,max.z+256--[[ 384--]] )))
        self.SoundTraceI = self.SoundTraceI+1
        if self.SoundTraceI>2 then self.SoundTraceI=0 end
        if upt > 350 then
            local coeff =
                1-math.min(
                    (math.min(130,leftt)/130+math.min(130,rightt)/130)/2,
                    math.Clamp((leftst-10)/40,0,1),
                    math.Clamp((rightst-10)/40,0,1)
                )
                --print(math.Clamp((leftst-10)/40,0,1))
            --print(Format("%02d %.2f %02d %.2f",leftst,math.Clamp((leftst-30)/20,0,1),rightst,)
            --[[ if leftst < 30 or rightst < 30 then
                LocalPlayer():ChatPrint(Format("I AM ON A STREET STATION, %.2f",coeff))
            elseif coeff > 1.3 then
                LocalPlayer():ChatPrint(Format("I AM ON A STREET, %.2f",coeff))
            else
                LocalPlayer():ChatPrint(Format("I AM ON A STREET WITH WALLS, %.2f",coeff))
            end--]]
            self.TunnelCoeff = math.Clamp(self.TunnelCoeff+(              0-self.TunnelCoeff)*self.DeltaTime*4,0,1)
            self.StreetCoeff = math.Clamp(self.StreetCoeff+((0.8+coeff*0.2)-self.StreetCoeff)*self.DeltaTime*4,0,1)
            self.Street = 1
        else
            local coeff =
                1-math.min(
                    math.Clamp((leftt-80)/40,0,1)+math.Clamp((rightt-80)/40,0,1)/2,
                    (math.Clamp((leftst-10)/40,0,1)+math.Clamp((rightst-10)/40,0,1))/2
                    --,
                )--(math.Clamp((leftst-30)/20,0,1)+math.Clamp((rightst-30)/20,0,1))*0.6
            --[[ if (leftst < 30 or rightst < 30) and coeff > 1.2 then
                LocalPlayer():ChatPrint(Format("I AM ON A STATION L%.2f R%.2f C:%.2f",leftt/55,rightt/55,coeff))
            elseif coeff > 1.3 then
                LocalPlayer():ChatPrint(Format("I AM IN A BIG TUNNEL L%.2f R%.2f C:%.2f",leftt/55,rightt/55,coeff))
            else
                LocalPlayer():ChatPrint(Format("I AM IN A TUNNEL L%.2f R%.2f C:%.2f",leftt/55,rightt/55,coeff))
            end--]]
            self.TunnelCoeff = math.Clamp(self.TunnelCoeff+((0.4+coeff*0.6)-self.TunnelCoeff)*self.DeltaTime*4,0,1)
            self.StreetCoeff = math.Clamp(self.StreetCoeff+((0.5-math.max(0,self.TunnelCoeff-0.5))-self.StreetCoeff)*self.DeltaTime*4,0,1)
            self.Street = 0
        end
    end
    if not self.HandleMouseInput and self.ButtonMap then
        if self == LocalPlayer().InMetrostroiTrain then
            for kp,pan in pairs(self.ButtonMap) do
                if not self:ShouldDrawPanel(kp) then continue end
                --If player is looking at this panel
                if pan.mouse and not pan.outside and pan.aimX and pan.aimY then
                    local aimX,aimY = math.floor(math.Clamp(pan.aimX,0,pan.width)),math.floor(math.Clamp(pan.aimY,0,pan.height))
                    if pan.OldAimX ~= aimX or pan.OldAimY ~= aimY then
                        net.Start("metrostroi-mouse-move",true)
                            net.WriteEntity(self)
                            net.WriteString(kp)
                            net.WriteFloat(aimX)
                            net.WriteFloat(aimY)
                        net.SendToServer()
                        pan.OldAimX = aimX
                        pan.OldAimY = aimY
                    end
                end
            end
        end
    end

    if self.ButtonMap and (not self.LastCheck or RealTime()-self.LastCheck > 0.5) then
        self.LastCheck = RealTime()
        local screenshotMode = C_ScreenshotMode:GetBool()
        if self.ScreenshotMode ~= screenshotMode then
            self:SetLOD(screenshotMode and 0 or -1)
            for k,cent in pairs(self.ClientEnts) do
                if IsValid(cent) then
                    cent:SetLOD(screenshotMode and 0 or -1)
                end
            end
            self.ScreenshotMode = screenshotMode
        end
        for k in pairs(self.HiddenLamps) do
            self.HiddenLamps[k] = false
        end
        for k,v in pairs(self.ClientProps) do
            if not v.pos then continue end
            local cent = self.ClientEnts[k]

            if (v.nohide or screenshotMode) then
                if not IsValid(cent) then
                    self:SpawnCSEnt(k,true)
                end
                continue
            end
            local hidden = not self:ShouldDrawClientEnt(k,v)
            if IsValid(cent) and hidden then
                cent:Remove()
                self.ClientEnts[k] = nil
            elseif not IsValid(cent) and not hidden then
                self:SpawnCSEnt(k,true)
            end
            if v.lamps and hidden then
                for i,k in ipairs(v.lamps) do
                    self:SetLightPower(k,false)
                    self.HiddenLamps[k] = true
                end
            end
        end
        for k,v in pairs(self.Sounds) do
            if type(v) ~= "function" and type(v) ~= "table" and not self.Sounds.isloop[k] and (not IsValid(v) or v:GetState() == GMOD_CHANNEL_STOPPED) then
                self:DestroySound(v)
                self.Sounds[k] = nil
            end
        end
        for k,v in pairs(self.ButtonMap) do
            if not v.pos then continue end

            if not v.hide or (v.nohide or screenshotMode) then
                self.HiddenPanelsDistance[k] = v.screenHide
                continue
            end
            self.HiddenPanelsDistance[k] = not self:ShouldDrawClientEnt(k,self.ButtonMap[k])
        end
    end

    if self.AutoAnims && self.AutoAnimNames then
        local aAnims = self.AutoAnims
        local aAnimNames = self.AutoAnimNames
        local hidden = self.Hidden
        for i=1, #aAnims do
            if not aAnimNames[i] or not hidden[aAnimNames[i]] then
                aAnims[i](self)
            end
        end
    end
    if self.Lights and self.GlowingLights then
        for id, light in pairs(self.GlowingLights) do
            if light.Update then
                light:Update()
            end
        end
    end
    -- Update passengers
    if self.RenderClientEnts and self.PassengerEnts then
        local stucked = self.PassengerEntsStucked
        for i,v in ipairs(self.LeftDoorPositions) do
            if self:GetPackedBool("DoorLS"..i) and not IsValid(stucked[i]) then
                local ent = ClientsideModel(table.Random(self.PassengerModels),RENDERGROUP_OPAQUE)
                ent:SetPos(self:LocalToWorld(Vector(v.x,v.y,self:GetStandingArea().z)))
                ent:SetAngles(self:LocalToWorldAngles(Angle(0,v.y < 0 and -90 or 90,0)))
                ent:SetSkin(math.floor(ent:SkinCount()*math.random()))
                ent:SetModelScale(0.98 + (-0.02+0.04*math.random()),0)
                ent:SetParent(self)
                stucked[i] = ent
                if math.random() > 0.99 then
                    self:PlayOnceFromPos("PassStuckL"..i,"subway_trains/common/door/pass_stAAAck.mp3",5,0.9+math.random()*0.2,150,400,v)
                elseif math.random() > 0.95 then
                    self:PlayOnceFromPos("PassStuckL"..i,"subway_trains/common/door/tom.mp3",5,0.9+math.random()*0.2,150,400,v)
                elseif ent:GetModel():find("models/metrostroi/passengers/f") then
                    self:PlayOnceFromPos("PassStuckL"..i,"subway_trains/common/door/pass_stuck.mp3",5,1.6+math.random()*0.2,150,400,v)
                else
                    self:PlayOnceFromPos("PassStuckL"..i,"subway_trains/common/door/pass_stuck.mp3",5,0.9+math.random()*0.2,150,400,v)
                end
            elseif not self:GetPackedBool("DoorLS"..i) and IsValid(stucked[i]) then
                SafeRemoveEntity(stucked[i])
            end
        end
        for i,v in ipairs(self.RightDoorPositions) do
            if self:GetPackedBool("DoorRS"..i) and not IsValid(stucked[-i]) then
                local ent = ClientsideModel(table.Random(self.PassengerModels),RENDERGROUP_OPAQUE)
                ent:SetPos(self:LocalToWorld(Vector(v.x,v.y,self:GetStandingArea().z)))
                ent:SetAngles(self:LocalToWorldAngles(Angle(0,v.y < 0 and -90 or 90,0)))
                ent:SetSkin(math.floor(ent:SkinCount()*math.random()))
                ent:SetModelScale(0.98 + (-0.02+0.04*math.random()),0)
                ent:SetParent(self)
                stucked[-i] = ent
                if math.random() > 0.99 then
                    self:PlayOnceFromPos("PassStuckR"..i,"subway_trains/common/door/pass_stAAAck.mp3",5,0.9+math.random()*0.2,150,400,v)
                elseif math.random() > 0.95 then
                    self:PlayOnceFromPos("PassStuckR"..i,"subway_trains/common/door/tom.mp3",5,0.9+math.random()*0.2,150,400,v)
                elseif ent:GetModel():find("models/metrostroi/passengers/f") then
                    self:PlayOnceFromPos("PassStuckR"..i,"subway_trains/common/door/pass_stuck.mp3",5,1.6+math.random()*0.2,150,400,v)
                else
                    self:PlayOnceFromPos("PassStuckR"..i,"subway_trains/common/door/pass_stuck.mp3",5,0.9+math.random()*0.2,150,400,v)
                end
            elseif not self:GetPackedBool("DoorRS"..i) and IsValid(stucked[-i]) then
                SafeRemoveEntity(stucked[-i])
            end
        end
        if #self.PassengerEnts ~= self:GetNW2Float("PassengerCount") then
            -- Passengers go out
            while #self.PassengerEnts > self:GetNW2Float("PassengerCount") do
                local ent = self.PassengerEnts[#self.PassengerEnts]
                table.remove(self.PassengerPositions,#self.PassengerPositions)
                table.remove(self.PassengerEnts,#self.PassengerEnts)
                ent:Remove()
            end
            -- Passengers go in
            while #self.PassengerEnts < self:GetNW2Float("PassengerCount") do
                local min,max = self:GetStandingArea()
                local pos = min + Vector((max.x-min.x)*math.random(),(max.y-min.y)*math.random(),(max.z-min.z)*math.random())

                --local ent = ents.CreateClientProp("models/metrostroi/81-717/reverser.mdl")
                --ent:SetModel(table.Random(self.PassengerModels))
                local ent = ClientsideModel(table.Random(self.PassengerModels),RENDERGROUP_OPAQUE)
                ent:SetPos(self:LocalToWorld(pos))
                ent:SetAngles(Angle(0,math.random(0,360),0))
                --[[
                hook.Add("MetrostroiBigLag",ent,function(ent)
                    ent:SetPos(self:LocalToWorld(pos))
                    ent:SetAngles(Angle(0,math.random(0,360),0))
                    --if ent.Spawned then hook.Remove("MetrostroiBigLag",ent) end
                    --ent.Spawned = true
                end)]]
                ent:SetSkin(math.floor(ent:SkinCount()*math.random()))
                ent:SetModelScale(0.98 + (-0.02+0.04*math.random()),0)
                ent:SetParent(self)
                table.insert(self.PassengerPositions,pos)
                table.insert(self.PassengerEnts,ent)
            end
        end
    end
    for k,v in pairs(self.CustomThinks) do if k ~= "BaseClass" then v(self) end end
end
function ENT:BlockInput(block)
    if IsValid(LocalPlayer().InMetrostroiTrain) then
        if self ~= LocalPlayer().InMetrostroiTrain then
            block = false
        end
    end
    if block and not self.GUILocker then
        gui.EnableScreenClicker(true)
        self.GUILocker = vgui.Create("DPanel")

        self.GUILocker:SetPos(0,0)
        self.GUILocker:SetSize(ScrW(),ScrH())
        self.GUILocker:SetZPos(-32767)

        self.GUILocker.Paint = function() end

        self.GUILocker.Focus = self

        self.GUILocker.Think = function(panel)
            if panel.Focus ~= vgui.GetKeyboardFocus() then
                panel.Focus = vgui.GetKeyboardFocus()
                if IsValid(panel.Focus) then
                    self.GUILocker:SetCursor("")
                else
                    input.SetCursorPos( ScrW()/2, ScrH()/2)
                    self.GUILocker:SetCursor("blank")
                end
            end
        end
        self.GUILocker.OnCursorMoved = function(panel,cursorX,cursorY )
            if self.GUILocker.LastX ~= cursorX or self.GUILocker.LastY ~= cursorY then
                if not IsValid(vgui.GetKeyboardFocus()) then
                    local x,y = ScrW()/2, ScrH()/2
                    input.SetCursorPos(x,y)
                    net.Start("metrostroi-mouse-move",true)
                        net.WriteEntity(self)
                        net.WriteString("")
                        net.WriteFloat((cursorX-x)/(ScrW()/2))
                        net.WriteFloat((cursorY-y)/(ScrH()/2))
                    net.SendToServer()
                end
                self.GUILocker.LastX = cursorX
                self.GUILocker.LastY = cursorY
            end
            --end
        end
        self.GUILocker:RequestFocus()
        self.GUILocker:SetKeyboardInputEnabled(false)
        self.GUILocker:MoveToBack(true)
        self.GUILocker:SizeToChildren()
    elseif not block and self.GUILocker then
        self.GUILocker:Remove()
        self.GUILocker = nil
        gui.EnableScreenClicker(false)
    end
end

function ENT:HandleMouse(handle)
    if self.HandleMouseInput ~= handle then
        self.HandleMouseInput = handle
        self:BlockInput(self.HandleMouseInput)
    end
end

local function compensateSeat(val)
    local valAbs = math.abs(val)
    if valAbs < 10 then return 1 end
    if valAbs > 45 then return 0 end

    local sign = val < 0 and -1 or 1
    return 1-math.Clamp((valAbs-10)/6,0,2)+(math.Clamp((valAbs-30)/10,0,1))+(math.Clamp((valAbs-40)/5,0,1))
end

local OldTrainHandle,OldSeat
hook.Add("Think","metrostroi_mouse_handle",function()
    local train, outside = isValidTrainDriver(LocalPlayer())
    if outside then train = nil end

    if OldTrainHandle ~= train then
        if IsValid(OldTrainHandle) and OldTrainHandle.BlockInput then OldTrainHandle:BlockInput(false) end
        if IsValid(train) and train.BlockInput then train:BlockInput(train.HandleMouseInput) end
        if IsValid(train) then
            OldSeat = LocalPlayer():GetVehicle()
            --[=[train.CamAnglesComp = Angle(0,0,0)
            train.OldAng = false
            --OldSeat.CalcAbsolutePosition = function(...) return ... end
            if not OldSeat.OldCalcAbsolutePosition then
                OldSeat.OldCalcAbsolutePosition = OldSeat.CalcAbsolutePosition
            end
            OldSeat.CalcAbsolutePosition = function(ent,...)
                --[[local target_ang = Angle(0,0,0)
                local train = ent:GetNW2Entity("TrainEntity")
                if not IsValid(train) then return end
                target_ang:RotateAroundAxis(ent:GetAngles():Forward(),-train.CamAng.p)
                target_ang:RotateAroundAxis(ent:GetAngles():Up(),train.CamAng.y)
                target_ang:RotateAroundAxis(ent:GetAngles():Right(),train.CamAng.r)
                train.CamAnglesComp = target_ang
                print(target_ang)]]
                return ent:OldCalcAbsolutePosition(...)
            end
            print(OldSeat.OnAngleChangeID)--]=]
        elseif IsValid(OldSeat) then
            OldSeat.CalcAbsolutePosition = OldSeat.OldCalcAbsolutePosition or OldSeat.CalcAbsolutePosition
            OldSeat.OldCalcAbsolutePosition = nil
            OldSeat = nil
        end
        OldTrainHandle = train
    end
end)
--[[hook.Add("PlayerEnteredVehicle","metrostroi_mouse_handle",function(ply,veh)
    local train = veh:GetNW2Entity("TrainEntity")
    if IsValid(train) then
        train.CamAnglesComp = Angle(0,0,0)
        train.OldAng = false
        if train.BlockInput then train:BlockInput(train.HandleMouseInput) end
    end
end)
hook.Add("PlayerEnteredVehicle","metrostroi_mouse_handle",function(ply,veh)
    local train = veh:GetNW2Entity("TrainEntity")
    if IsValid(train) then
        train.CamAnglesComp = Angle(0,0,0)
        train.OldAng = false
        if train.BlockInput then train:BlockInput(train.HandleMouseInput) end
    end
end)]]
--[[ hook.Add("PlayerEnteredVehicle","metrostroi_mouse_handle",function(ply,veh)
    local train = veh:GetNW2Entity("TrainEntity")
    if IsValid(train) and train.BlockInput then
        train:BlockInput(train.HandleMouseInput)
    end
end)--]]
--------------------------------------------------------------------------------
-- Various rendering shortcuts for trains
--------------------------------------------------------------------------------
function ENT:DrawCircle(cx,cy,radius)
    local step = 2*math.pi/12
    local vertexBuffer = { {}, {}, {} }

    for i=1,12 do
        vertexBuffer[1].x = cx + radius*math.sin(step*(i+0))
        vertexBuffer[1].y = cy + radius*math.cos(step*(i+0))
        vertexBuffer[2].x = cx
        vertexBuffer[2].y = cy
        vertexBuffer[3].x = cx + radius*math.sin(step*(i+1))
        vertexBuffer[3].y = cy + radius*math.cos(step*(i+1))
        surface.DrawPoly(vertexBuffer)
    end
end

--------------------------------------------------------------------------------
-- Schedule Drawing
--
-- Reference: http://static.diary.ru/userdir/1/0/4/7/1047/28088395.jpg
--------------------------------------------------------------------------------
local function AddZero( s )
    if #s == 0 then
        return "00"
    elseif #s == 1 then
        return "0" .. s
    else
        return s
    end
end

local function HoursFromStamp( stamp )
    return AddZero(tostring(math.floor(stamp/3600)%24))
end

local function MinutesFromStamp( stamp )
    return AddZero(tostring(math.floor(stamp/60)%60))
end

local function SecondsFromStamp( stamp )
    return AddZero(tostring(stamp%60))
end

surface.CreateFont( "Schedule_Hand", {
    font = "Monotype Corsiva",
    size = 30,
    weight = 600
})
surface.CreateFont( "Schedule_Hand_Small", {
    font = "Monotype Corsiva",
    size = 18,
    weight = 600
})
surface.CreateFont( "Schedule_Machine", {
    font = "Arial",
    size = 22,
    weight = 500
})
surface.CreateFont( "Schedule_Machine_Small", {
    font = "Arial",
    size = 16,
    weight = 600
})

local DrawRect = surface.DrawRect
local DrawTextHand = function(txt, x, y, col)
    draw.SimpleText(txt, "Schedule_Hand", x, y, Color(0,15*col.y,85*col.z), 0, 0)
end
local DrawTextHandSmall = function(txt, x, y, col)
    draw.SimpleText(txt, "Schedule_Hand_Small", x, y, Color(0,15*col.y,85*col.z), 0, 0)
end
local DrawTextMachine = function(txt, x, y)
    draw.SimpleText(txt, "Schedule_Machine", x, y, Color(0,0,0), 0, 0)
end
local DrawTextMachineSmall = function(txt, x, y)
    draw.SimpleText(txt, "Schedule_Machine_Small", x, y, Color(0,0,0), 0, 0)
end

local function FineStationName(st)
    local StT = string.Explode(" ",st)
    local str = ""
    if #StT > 1 then
        str = StT[1][1]..". "..table.concat(StT," ",2)
    else
        str = st
    end
    return str
end
-- Placeholder code, to be removed when schedule system is in place
local Schedule = {
    stations = {
        {"Station 1", os.time() + 20},
        {"Station 2", os.time() + 46},
        {"Station 3", os.time() + 80},
        {"Station 4", os.time() + 95},
        {"Station 5", os.time() + 120}
    },
    total = 2000,
    interval = 300,
    routenumber = math.random(100,999),
    pathnumber = math.random(100,999)
}

local col1w = 80 -- 1st Column width
local col2w = 32 -- The other column widths
local rowtall = 30 -- Row height, includes -only- the usable space and not any lines
local rowtall2 = rowtall*2 -- Helper

local defaultlight = Vector(0.8,0.8,0.8) -- Light to be used when cabinlights are on
function ENT:DrawSchedule(panel)
    local w = panel.width
    local h = panel.height

    local light = defaultlight
    local cabinlights = self:GetPackedBool(58)
    if not cabinlights then
        light = render.GetLightColor(self:LocalToWorld(Vector(430,0,26))) -- GetLightColor is pretty shit but it works
    end

    --Background
    surface.SetDrawColor(Color(255 * light.x, 253 * light.y, 208 * light.z))
    DrawRect(0,0,w,h)

    --Lines
    surface.SetDrawColor(Color(0,0,0))

    --Horisontal lines
    DrawRect(0,0,1,h)
    DrawRect(1 + col1w,0,1,h)
    DrawRect(1 + col1w + 1 + col2w,rowtall2+2,1,h-rowtall2-2)
    DrawRect(1 + col1w + 1 + col2w + 1 + col2w,rowtall2+2,1,h-rowtall2-2)
    DrawRect(1 + col1w + 1 + col2w + 1 + col2w + 1 + col2w,0,1,h)

    --Vertical lines
    DrawRect(0,0,w,1)
    DrawRect(1 + col1w,rowtall+1,w - col1w - 1,1)
    DrawRect(1 + col1w,rowtall2+2,w - col1w - 1,1)
    for i=(rowtall+1)*3,h,rowtall+1 do
        DrawRect(0,i,w,1)
    end

    -- HACK get schedule from train
    local N = self:GetNW2Int("_schedule_N")
    Schedule = {
        stations = {},
        total = math.floor(self:GetNW2Int("_schedule_duration")/5+0.5)*5,
        interval = self:GetNW2Int("_schedule_interval"),
        routenumber = self:GetNW2Int("_schedule_id"),
        pathnumber = self:GetNW2Int("_schedule_path"),
    }
    for i=1,N do
        Schedule.stations[i] = {
            self:GetNW2String("_schedule_"..i.."_5"),
            math.floor(self:GetNW2Int("_schedule_"..i.."_3")*60/5)*5
        }
    end

    --Text
    local t = Schedule

    --Top info
    DrawTextMachine("М №", 3, 3)
    DrawTextHand(t.routenumber, 42, -2, light)

    DrawTextMachine("П №", 3, rowtall*2 + 3)
    DrawTextHand(t.pathnumber, 42, rowtall*2 - 2, light)

    DrawTextMachineSmall("ВРЕМЯ", col1w + 5, 1, light)
    DrawTextMachineSmall("ХОДА", col1w + 5, 15, light)
    DrawTextHand(MinutesFromStamp(t.total), w - 50, 1, light)
    DrawTextHandSmall(SecondsFromStamp(t.total), w - 25, 5, light)

    DrawTextMachineSmall("ИНТ", col1w + 5, rowtall + 8)
    DrawTextHand(MinutesFromStamp(t.interval), w - 50, rowtall, light)
    DrawTextHandSmall(SecondsFromStamp(t.interval), w - 25, rowtall + 4, light)

    DrawTextMachineSmall("ЧАС", col1w + 4, rowtall*2    + 8)
    DrawTextMachineSmall("МИН", col1w + col2w + 5, rowtall*2 + 8)
    DrawTextMachineSmall("СЕК", col1w + col2w*2 + 8, rowtall*2 + 8)

    --Schedule rows
    local lasthour = -1
    for i,v in pairs(t.stations) do
        local y = ((rowtall+1)*3+2) + (i-1)*(rowtall+1) -- Uhh..

        local st = FineStationName(v[1])
        surface.SetFont( "Schedule_Machine_Small" )
        local width = select(1, surface.GetTextSize(st))

        local szf = math.ceil(width/80)-1
        if szf > 0 then
            szf = math.ceil(#st/8)-1

            for i1 = 0,szf do
                DrawTextMachineSmall(st:Replace("'",""):sub(i1*8+1,8 + i1*8)..(szf ~= i1 and "-" or ""), 3, y + 6 -6 + 12/szf*i1) -- Stationname
            end
        else
            DrawTextMachineSmall(st, 3, y + 6) -- Stationname
        end

        local hours = HoursFromStamp(v[2])
        local minutes = MinutesFromStamp(v[2])
        local seconds = SecondsFromStamp(v[2])

        if hours ~= lasthour then -- Only draw hours if they've changed
            lasthour = hours

            DrawTextHand(hours, col1w + 3, y, light) -- Hours
        end

        DrawTextHand(minutes, col1w + col2w + 5, y, light) -- Minutes
        DrawTextHand(seconds, col1w + col2w + col2w + 5, y, light) -- Seconds
    end
end

--------------------------------------------------------------------------------
-- Default rendering function
--------------------------------------------------------------------------------
function ENT:Draw()

    -- Draw model
    self:DrawModel()
end


function ENT:DrawOnPanel(index,func,overr)
    if not overr and not self:ShouldDrawPanel(index) then return end
    local panel = self.ButtonMapMatrix and self.ButtonMapMatrix[index] or self.ButtonMap[index]
    cam.Start3D2D(self:LocalToWorld(panel.pos),self:LocalToWorldAngles(panel.ang),panel.scale)
        func(panel)
    cam.End3D2D()
end

function ENT:DrawRTOnPanel(index,rt,overr)
    if not overr and not self:ShouldDrawPanel(index) then return end
    local panel = self.ButtonMapMatrix[index] or self.ButtonMap[index]
    cam.Start3D2D(self:LocalToWorld(panel.pos),self:LocalToWorldAngles(panel.ang),panel.scale)
        surface.SetMaterial(rt.mat)
        --surface.DrawTexturedRect(0,0,panel.width,panel.height)
        surface.DrawTexturedRectRotated(panel.width/2,panel.height/2,panel.width,panel.height,0)
    cam.End3D2D()
end


--------------------------------------------------------------------------------
-- Animation function
--------------------------------------------------------------------------------
function ENT:Animate(clientProp, value, min, max, speed, damping, stickyness)
    local id = clientProp
    local anims = self.Anims
    if not anims[id] then
        anims[id] = {}
        anims[id].val = value
        anims[id].value = min + (max-min)*value
        anims[id].V = 0.0
        anims[id].block = false
        anims[id].stuck = false
        anims[id].P = value
    end
    if self.Hidden[id] or self.Hidden.anim[id] then return 0 end
    if anims[id].Ignore then
        if RealTime()-anims[id].Ignore < 0 then
            return anims[id].value
        else
            anims[id].Ignore = nil
        end
    end
    local val = anims[id].val
    if value ~= val then
        anims[id].block = false
    end
    if anims[id].block then
        if anims[id].reload and IsValid(self.ClientEnts[clientProp]) then
            self.ClientEnts[clientProp]:SetPoseParameter("position",anims[id].value)
            anims[id].reload = false
        end
        return anims[id].value--min + (max-min)*anims[id].val
    end
    --if self["_anim_old_"..id] == value then return self["_anim_old_"..id] end
    -- Generate sticky value
    if stickyness and damping then
        if (math.abs(anims[id].P - value) < stickyness) and (anims[id].stuck) then
            value = anims[id].P
            anims[id].stuck = false
        else
            anims[id].P = value
        end
    end
    local dT = FrameTime()--self.DeltaTime
    if damping == false then
        local dX = speed * dT
        if value > val then
            val = val + dX
        end
        if value < val then
            val = val - dX
        end
        if math.abs(value - val) < dX then
            val = value
            anims[id].V = 0
        else
            anims[id].V = dX
        end
    else
        -- Prepare speed limiting
        local delta = math.abs(value - val)
        local max_speed = 1.5*delta / dT
        local max_accel = 0.5 / dT

        -- Simulate
        local dX2dT = (speed or 128)*(value - val) - anims[id].V * (damping or 8.0)
        if dX2dT >  max_accel then dX2dT =  max_accel end
        if dX2dT < -max_accel then dX2dT = -max_accel end

        anims[id].V = anims[id].V + dX2dT * dT
        if anims[id].V >  max_speed then anims[id].V =  max_speed end
        if anims[id].V < -max_speed then anims[id].V = -max_speed end

        val = math.max(0,math.min(1,val + anims[id].V * dT))

        -- Check if value got stuck
        if (math.abs(dX2dT) < 0.001) and stickyness and (dT > 0) then
            anims[id].stuck = true
        end
    end
    local retval = min + (max-min)*val
    if IsValid(self.ClientEnts[clientProp]) then
        self.ClientEnts[clientProp]:SetPoseParameter("position",retval)
    end
    if math.abs(anims[id].V) == 0 and math.abs(val-value) == 0 and not anims[id].stuck then
        anims[id].block = true
    end

    anims[id].val = val
    anims[id].oldival = value
    anims[id].oldspeed = speed
    anims[id].value = retval
    return retval
end
function ENT:AnimateFrom(clientProp,from,min,max)
    if not self.Anims[from] then return 0 end
    local val = Lerp(self.Anims[from].value,min or 0,max or 1)
    if IsValid(self.ClientEnts[clientProp]) then
        self.ClientEnts[clientProp]:SetPoseParameter("position",val)
    end
    if not self.Anims[clientProp] then self.Anims[clientProp] = {} end
    self.Anims[clientProp].value = value
    return val
end

function ENT:ShowHide(clientProp, value, over)
    if self.Hidden.override[clientProp] then return end
    --if IsValid(self.ClientEnts[clientProp]) then
    if value == true and (self.Hidden[clientProp] or over) then
        self.Hidden[clientProp] = false
        if not IsValid(self.ClientEnts[clientProp]) and self:SpawnCSEnt(clientProp) then
            self.UpdateRender = true
        end
        --self.ClientEnts[clientProp]:SetRenderMode(RENDERMODE_NORMAL)
        --self.ClientEnts[clientProp]:SetColor(Color(255,255,255,255))
        --self.Hidden[clientProp] = false
        return true
    elseif value ~= true and (not self.Hidden[clientProp] or over) then
        if IsValid(self.ClientEnts[clientProp]) then
            self.ClientEnts[clientProp]:Remove()
            self.UpdateRender = true
        end
        self.Hidden[clientProp] = true
        --self.ClientEnts[clientProp]:SetRenderMode(RENDERMODE_NONE)
        --self.ClientEnts[clientProp]:SetColor(Color(0,0,0,0))
        --self.Hidden[clientProp] = true
        return true
    end
        --self.HiddenQuele[clientProp] = nil
    --else
    --end
end

function ENT:HideButton(clientProp, value)
    self.Hidden.button[clientProp] = value
end
function ENT:ShowHideSmooth(clientProp, value,color)
    if self.Hidden.override[clientProp] then return value end
    if not IsValid(self.ClientEnts[clientProp]) and self.SmoothHide[clientProp] then self.SmoothHide[clientProp] = 0 end
    if self.SmoothHide[clientProp] and (self.SmoothHide[clientProp] == value and not color) then return value end
    self.SmoothHide[clientProp] = value
    self.Hidden.anim[clientProp] = value == 0

    if value > 0 and not IsValid(self.ClientEnts[clientProp]) then
        if self:ShowHide(clientProp,true) then self.SmoothHide[clientProp] = nil end
    end
    if value == 0 and IsValid(self.ClientEnts[clientProp]) then
        if self:ShowHide(clientProp,false) then self.SmoothHide[clientProp] = nil end
    end
    if IsValid(self.ClientEnts[clientProp]) then
        local v = self.ClientPropsOv and self.ClientPropsOv[clientProp] or self.ClientProps[clientProp]
        self.ClientEnts[clientProp]:SetRenderMode(RENDERMODE_TRANSALPHA)
        if color then
            self.ClientEnts[clientProp]:SetColor(ColorAlpha(color,value*255))
        else
            self.ClientEnts[clientProp]:SetColor(ColorAlpha(v.color or color_white,value*255))
        end
    end
    return value
end
function ENT:ShowHideSmoothFrom(clientProp,from)
    self:ShowHideSmooth(clientProp,self.SmoothHide[from] or 0)
end
local digit_bitmap = {
    [1] = { 0,0,1,0,0,1,0 },
    [2] = { 1,0,1,1,1,0,1 },
    [3] = { 1,0,1,1,0,1,1 },
    [4] = { 0,1,1,1,0,1,0 },
    [5] = { 1,1,0,1,0,1,1 },
    [6] = { 1,1,0,1,1,1,1 },
    [7] = { 1,0,1,0,0,1,0 },
    [8] = { 1,1,1,1,1,1,1 },
    [9] = { 1,1,1,1,0,1,1 },
    [0] = { 1,1,1,0,1,1,1 },
}

local segment_poly = {
    [1] = {
        { x = 0,    y = 0 },
        { x = 100,  y = 0 },
        { x =  80,  y = 20 },
        { x =  20,  y = 20 },
    },
    [2] = {
        { x =  20,  y = 0 },
        { x =  80,  y = 0 },
        { x = 100,  y = 20 },
        { x =   0,  y = 20 },
    },
    [3] = {
        { x =  0,  y = 0 },
        { x = 20,  y = 20 },
        { x = 20,  y = 80 },
        { x =  0,  y = 100 },
    },
    [4] = {
        { x =  0,  y = 20 },
        { x = 20,  y = 0 },
        { x = 20,  y = 100 },
        { x =  0,  y = 80 },
    },
    [5] = {
        { x = 0,  y = 12 },
        { x = 20,  y = 0 },
        { x = 80,  y = 0 },
        { x = 100,  y = 12 },
        { x = 80,  y = 24 },
        { x = 20,  y = 24 },
    },
}

local polys = {}
function ENT:DrawSegment(i,x,y,scale_x,scale_y)
    if not polys[i] then polys[i] = {} end
    if not polys[i][k] then
        for k,v in pairs(segment_poly[i]) do
            polys[i][k] = {
                x = (v.x*scale_x) + x,
                y = (v.y*scale_y) + y,
            }
        end
    end

    surface.SetDrawColor(Color(100,255,0,255))
    draw.NoTexture()
    surface.DrawPoly(polys[i])
end

function ENT:DrawDigit(cx,cy,digit,scalex,scaley,thickness)
    scalex = scalex or 1
    scaley = scaley or scalex
    thickness = thickness or 1
    local bitmap = digit_bitmap[digit]
    if not bitmap then return end

    local sx = 0.9*scalex*thickness
    local sy = 0.9*scaley*thickness
    local dx = scalex
    local dy = scaley

    if bitmap[1] == 1 then self:DrawSegment(1,cx+5*dx,cy,           sx,sy)  end
    if bitmap[2] == 1 then self:DrawSegment(3,cx,cy+10*dy,          sx,sy)  end
    if bitmap[3] == 1 then self:DrawSegment(4,cx+80*dx,cy+10*dy,    sx,sy)  end
    if bitmap[4] == 1 then self:DrawSegment(5,cx+5*dx,cy+95*dy,     sx,sy)  end
    if bitmap[5] == 1 then self:DrawSegment(3,cx,cy+110*dy,         sx,sy)  end
    if bitmap[6] == 1 then self:DrawSegment(4,cx+80*dx,cy+110*dy,   sx,sy)  end
    if bitmap[7] == 1 then self:DrawSegment(2,cx+5*dx,cy+190*dy,    sx,sy)  end
end



--------------------------------------------------------------------------------
-- Get train acceleration at given position in train
--------------------------------------------------------------------------------
function ENT:GetTrainAccelerationAtPos(pos)
    local localAcceleration = self:GetTrainAcceleration()
    local angularVelocity = self:GetTrainAngularVelocity()

    return localAcceleration - angularVelocity:Cross(angularVelocity:Cross(pos*0.01905))
end


--------------------------------------------------------------------------------
-- Look into mirrors hook
--------------------------------------------------------------------------------
--[[hook.Add("InputMouseApply", "Metrostroi_TrainView", function(cmd,x,y,ang)
    local seat = LocalPlayer():GetVehicle()
    if (not seat) or (not seat:IsValid()) then
        return
    end
    local train = seat:GetNW2Entity("TrainEntity")
    if (not train) or (not train:IsValid()) then
        return
    end
    local target_ang = Angle(0,0,0)
    target_ang:RotateAroundAxis(seat:GetAngles():Forward(),-ang.p)
    target_ang:RotateAroundAxis(seat:GetAngles():Up(),ang.y)
    target_ang:RotateAroundAxis(seat:GetAngles():Right(),ang.r)
    train.CamAnglesComp = target_ang
    train.CamAng = ang
end)]]

hook.Add("CalcVehicleView", "Metrostroi_TrainView", function(seat,ply,tbl)
    local train = ply.InMetrostroiTrain
    if not IsValid(train) then
        return
    end

    --local hack = string.find(train:GetClass(),"81")
    --local dy = 0
    --if hack then dy = 3 end

    --[[-- Get acceleration in the train
    local headPos = train:WorldToLocal(pos)
    local acceleration = train:GetTrainAccelerationAtPos(headPos)
    train.Acceleration = train.Acceleration or Vector(0,0,0)
    train.Acceleration = train.Acceleration + 0.5*(acceleration - train.Acceleration)*train.DeltaTime
    if train.Acceleration:Length() > 100 then train.Acceleration = Vector(0,0,0) end

    -- Calculate direction
    local direction = train.Acceleration:GetNormalized()
    -- Calculate visual offset
    local a = train.Acceleration:Length()
    local factor = a * math.exp(-0.05*a)
    local offset = 4 * direction * factor

    print(train.Acceleration)
    -- Apply offset
    return {
        origin = train:LocalToWorld(headPos + 0.1*offset),
        angles = ang + Angle(offset.x,0,0),
    }]]--

    if seat:GetThirdPersonMode() and train.MirrorCams[1] then
        local trainAng = tbl.angles - train:GetAngles()
        if trainAng.y >  180 then trainAng.y = trainAng.y - 360 end
        if trainAng.y < -180 then trainAng.y = trainAng.y + 360 end
        if trainAng.y > 0 then
            train.CamPos =  train:LocalToWorld(train.MirrorCams[1])
            train.CamAngles =  train:LocalToWorldAngles(train.MirrorCams[2])
            return {
                origin = train.CamPos,
                angles = train.CamAngles,
                fov = train.MirrorCams[3],
            }
        else
            train.CamPos =  train:LocalToWorld(train.MirrorCams[4])
            train.CamAngles =  train:LocalToWorldAngles(train.MirrorCams[5])
            return {
                origin = train.CamPos,
                angles = train.CamAngles,
                fov = train.MirrorCams[6],
            }
        end
    elseif train.CurrentCamera > 0 and train.Cameras[train.CurrentCamera] then
        local camera = train.Cameras[train.CurrentCamera]
        train.CamPos = train:LocalToWorld(camera[1])
        local tFov = tbl.fov/C_FovDesired:GetFloat()*C_CabFOV:GetFloat()

        return {
            origin = train.CamPos,
            angles = tbl.angles,--+train:LocalToWorldAngles(camera[2]),
            fov = tFov,
        }
    else

        train.CamPos = train:LocalToWorld(train:WorldToLocal(tbl.origin)+Vector(train.HeadAcceleration,0,C_CabZ:GetFloat()))
        local tFov = tbl.fov/C_FovDesired:GetFloat()*C_CabFOV:GetFloat()
        return {
            origin = train.CamPos,
            angles = tbl.angles,--target_ang+train.CamAnglesComp,
            fov = tFov,
        }
    end
    return
end)




--------------------------------------------------------------------------------
-- Buttons/panel clicking
--------------------------------------------------------------------------------
--Thanks old gmod wiki!
--[[
Converts from world coordinates to Draw3D2D screen coordinates.
vWorldPos is a vector in the world nearby a Draw3D2D screen.
vPos is the position you gave Start3D2D. The screen is drawn from this point in the world.
scale is a number you also gave to Start3D2D.
aRot is the angles you gave Start3D2D. The screen is drawn rotated according to these angles.
]]--

local function WorldToScreen(vWorldPos, vPos, vScale, aRot)
    vWorldPos = vWorldPos - vPos
    vWorldPos:Rotate(Angle(0, -aRot.y, 0))
    vWorldPos:Rotate(Angle(-aRot.p, 0, 0))
    vWorldPos:Rotate(Angle(0, 0, -aRot.r))

    return vWorldPos.x / vScale, (-vWorldPos.y) / vScale
end

-- Calculates line-plane intersect location
local function LinePlaneIntersect(PlanePos,PlaneNormal,LinePos,LineDir)
    local dot = LineDir:Dot(PlaneNormal)
    local fac = LinePos-PlanePos
    local dis = -PlaneNormal:Dot(fac) / dot
    return LineDir * dis + LinePos
end

local function findAimButton(ply,train)
    local panel,panelDist = nil,1e9
    for kp,pan in pairs(train.ButtonMap) do
        if not train:ShouldDrawPanel(kp) then continue end
        --If player is looking at this panel
        if pan.aimedAt and (pan.buttons or pan.sensor or pan.mouse) and pan.aimedAt < panelDist then
            panel = pan
            panelDist = pan.aimedAt
        end
    end
    if not panel then return false end
    if panel.aimX and panel.aimY and (panel.sensor or panel.mouse) and math.InRangeXY(panel.aimX,panel.aimY,0,0,panel.width,panel.height) then return false,panel.aimX,panel.aimY,panel.system end
    if not panel.buttons then return false end

    local buttonTarget
    for _,button in pairs(panel.buttons) do
        if (train.Hidden[button.PropName] or train.Hidden.button[button.PropName]) and (not train.ClientProps[button.PropName] or not train.ClientProps[button.PropName].config or not train.ClientProps[button.PropName].config.staylabel) then continue end
        if (train.Hidden[button.ID] or train.Hidden.button[button.ID])  and (not train.ClientProps[button.ID] or not train.ClientProps[button.ID].config or not train.ClientProps[button.ID].config.staylabel) then  continue end
        if button.w and button.h then
            if  panel.aimX >= button.x and panel.aimX <= (button.x + button.w) and
                    panel.aimY >= button.y and panel.aimY <= (button.y + button.h) then
                buttonTarget = button
                --table.insert(foundbuttons,{button,panel.aimedAt})
            end
        else
            --If the aim location is withing button radis
            local dist = math.Distance(button.x,button.y,panel.aimX,panel.aimY)
            if dist < (button.radius or 10) then
                buttonTarget = button
                --table.insert(foundbuttons,{button,panel.aimedAt})
            end
        end
    end

    if not buttonTarget then return false end

    return buttonTarget
end

-- Checks what button/panel is being looked at and check for custom crosshair
hook.Add("Think","metrostroi-cabin-panel",function()
    local ply = LocalPlayer()
    if not IsValid(ply) then return end

    toolTipText = nil
    drawCrosshair = false
    canDrawCrosshair = false

    local train, outside = isValidTrainDriver(ply)
    if not IsValid(train) then return end
    if gui.IsConsoleVisible() or gui.IsGameUIVisible() or IsValid(vgui.GetHoveredPanel()) and not vgui.IsHoveringWorld() and  vgui.GetHoveredPanel():GetParent() ~= vgui.GetWorldPanel() then return end
    if train.ButtonMap ~= nil then
        canDrawCrosshair = true
        local plyaimvec
        if outside then
            plyaimvec = ply:GetAimVector()
        else
            local x,y = input.GetCursorPos()
            --plyaimvec = util.AimVector( train.CamAngles, train.CamFOV,x,y,ScrW(),ScrH())
            --plyaimvec = ply:GetAimVector()
            plyaimvec = gui.ScreenToVector(x,y) -- ply:GetAimVector() is unreliable when in seats
        end

        -- Loop trough every panel
        for k2,panel in pairs(train.ButtonMap) do
            if not train:ShouldDrawPanel(kp2) then continue end
            local pang = train:LocalToWorldAngles(panel.ang)

            if plyaimvec:Dot(pang:Up()) < 0 then
                local campos = not outside and train.CamPos or ply:EyePos()
                local ppos = train:LocalToWorld(panel.pos)-- - Vector(math.Round((not outside and train.HeadAcceleration or 0),2),0,0))
                local isectPos = LinePlaneIntersect(ppos,pang:Up(),campos,plyaimvec)
                local localx,localy = WorldToScreen(isectPos,ppos,panel.scale,pang)

                panel.aimX = localx
                panel.aimY = localy
                if plyaimvec:Dot(isectPos - campos)/(isectPos-campos):Length() > 0 and localx > 0 and localx < panel.width and localy > 0 and localy < panel.height then
                    panel.aimedAt = isectPos:Distance(campos)
                    drawCrosshair = panel.aimedAt
                else
                    panel.aimedAt = false
                end
                panel.outside = outside
            else
                panel.aimedAt = false
            end
        end

        -- Tooltips
        local ttdelay = GetConVar("metrostroi_tooltip_delay"):GetFloat()
        if GetConVar("metrostroi_disablehovertext"):GetInt() == 0 and ttdelay and ttdelay >= 0 then
            local button = findAimButton(ply,train)
            --print(train.ClientProps[button.ID].button)
            if button and
                ((train.Hidden[button.ID] or train.Hidden[button.PropName]) and (not train.ClientProps[button.ID].config or not train.ClientProps[button.ID].config.staylabel) or
                (train.Hidden.button[button.ID] or train.Hidden.button[button.PropName]) and (not train.ClientProps[button.PropName].config or not train.ClientProps[button.PropName].config.staylabel)) then
                return
            end
            if button ~= lastAimButton then
                lastAimButtonChange = CurTime()
                lastAimButton = button
            end

            if button then
                if ttdelay == 0 or CurTime() - lastAimButtonChange > ttdelay then
                    if C_DrawDebug:GetInt() > 0 then
                        toolTipText,toolTipColor = button.ID,Color(255,0,255)
                    elseif button.plombed then
                        toolTipText,_,toolTipColor = button.plombed(train)
                    else
                        toolTipText,toolTipColor = button.tooltip
                    end
                    --[[toolTipPosition = nil
                    if button.tooltipState then
                        local newTT,newTTpos = button.tooltipState(train)
                        toolTipText = toolTipText..newTT
                        toolTipPosition = Metrostroi.GetPhrase(newTTpos)
                    end]]
                    if GetConVar("metrostroi_disablehovertextpos"):GetInt() == 0 and button.tooltipState and button.tooltip then
                        toolTipText = toolTipText..button.tooltipState(train)
                    end
                end
            end
        end
    end
end)


-- Takes button table, sends current status
local function sendButtonMessage(button,train,outside)
    local tooltip,buttID = nil,button.ID
    if button.plombed then
        tooltip,buttID = button.plombed(train)
    end
    if not buttID then Error(Format("Can't send button message! %s\n",button.ID)) return end
    net.Start("metrostroi-cabin-button")
        net.WriteEntity(train)
        net.WriteString(buttID:gsub("^.+:",""))
        net.WriteBit(button.state)
        net.WriteBool(outside)
    net.SendToServer()
    return buttID
    --RunConsoleCommand("metrostroi_button_press",button.ID..(button.state and 1 or 0))
end
-- Takes button table, sends current status
local function sendPanelTouch(panel,x,y,outside,state)
    net.Start("metrostroi-panel-touch")
    net.WriteString(panel or "")
    net.WriteInt(x,11)
    net.WriteInt(y,11)
    net.WriteBool(outside)
    net.WriteBool(state)
    net.SendToServer()
    --RunConsoleCommand("metrostroi_button_press",button.ID..(button.state and 1 or 0))
end

-- Goes over a train's buttons and clears them, sending a message if needed
function ENT:ClearButtons()
    if self.ButtonMap == nil then return end
    for _,panel in pairs(self.ButtonMap) do
        if panel.buttons then
            for _,button in pairs(panel.buttons) do
                if button.state == true then
                    button.state = false
                    sendButtonMessage(button,self)
                end
            end
        end
    end
end

function ENT:HidePanel(kp,hide)
    if hide and not self.HiddenPanels[kp] then
        self.HiddenPanels[kp] = true
        if self.ButtonMap[kp].props then
            for _,v in pairs(self.ButtonMap[kp].props) do
                --self.Hidden[v] = true
                self:ShowHide(v,false,true)
                self.Hidden.override[v] = true
            end
        end
    end
    if not hide and self.HiddenPanels[kp] then
        self.HiddenPanels[kp] = nil
        if self.ButtonMap[kp].props then
            for _,v in pairs(self.ButtonMap[kp].props) do
                --self.Hidden[v] = false
                self.Hidden.override[v] = false
                self:ShowHide(v,true,true)
            end
        end
    end
end
-- Args are player, IN_ enum and bool for press/release
local function handleKeyEvent(ply,key,pressed)
    if not game.SinglePlayer() and not IsFirstTimePredicted() then return end
    if gui.IsConsoleVisible() or gui.IsGameUIVisible() or IsValid(vgui.GetHoveredPanel()) and not vgui.IsHoveringWorld() and  vgui.GetHoveredPanel():GetParent() ~= vgui.GetWorldPanel() then return end
    if key ~= MOUSE_LEFT and key ~= MOUSE_RIGHT then return end
    local train, outside = isValidTrainDriver(ply)

    if not IsValid(train) then return end
    if train.ButtonMap == nil then return end
    if key == MOUSE_LEFT and not pressed then train:ClearButtons() end
    if pressed then
        local button,x,y,system = findAimButton(ply,train)
        local plombed = false
        if button and button.ID and button.ID[1] ~= "!" and (key ~= MOUSE_LEFT or not button.plombed or not ({button.plombed(train)})[3]) then
            button.state = true
            local buttID = sendButtonMessage(button,train,outside)
            lastButton = button
            lastButton.train = train
            if train.OnButtonPressed then
                train:OnButtonPressed(buttID:gsub("^.+:",""))
            end
        elseif not button and x and y and not lastTouch then
            sendPanelTouch(system,x,y,outside,true)
            lastTouch = {system,x,y}
        end
    else
        -- Reset the last button pressed
        if lastButton ~= nil then
            if lastButton.state == true then
                lastButton.state = false
                sendButtonMessage(lastButton,lastButton.train,outside)
            end
            if train.OnButtonReleased and button then
                local tooltip,buttID = nil,button.ID
                if button.plombed then
                    tooltip,buttID = button.plombed(train)
                end
                train:OnButtonReleased(buttID:gsub("^.+:",""))
            end
        end
        if lastTouch ~= nil then
            sendPanelTouch(lastTouch[1],lastTouch[2],lastTouch[3],outside,false)
            lastTouch = nil
        end
    end
end

-- Hook for clearing the buttons when player exits
net.Receive("metrostroi-cabin-reset",function()
    local ent = net.ReadEntity()
    if IsValid(ent) and ent.ClearButtons ~= nil then
        ent:ClearButtons()
    end
end)

local lastChanged = RealTime()
local camAnim = 0
local camStart = 0
local camEnd = 1
local function handleCam(ply,button)
    if not game.SinglePlayer() and not IsFirstTimePredicted() then return end
    if not input.IsShiftDown() then return end
    local train, outside = isValidTrainDriver(ply)
    if not IsValid(train) or outside then return end
    if not train.Cameras then return end
    local oldCam = train.CurrentCamera
    if button == KEY_LEFT then
        repeat
            train.CurrentCamera = train.CurrentCamera - 1
            if train.CurrentCamera < 0 then
                train.CurrentCamera = #train.Cameras
            end
        until not train.Cameras[train.CurrentCamera] or not train.Cameras[train.CurrentCamera][4] or train:ShouldDrawPanel(train.Cameras[train.CurrentCamera][4])
    end
    if button == KEY_RIGHT then
        repeat
            train.CurrentCamera = train.CurrentCamera + 1
            if train.CurrentCamera > #train.Cameras then
                train.CurrentCamera = 0
            end
        until not train.Cameras[train.CurrentCamera] or not train.Cameras[train.CurrentCamera][4] or train:ShouldDrawPanel(train.Cameras[train.CurrentCamera][4])
    end
    if button == KEY_DOWN then
        train.CurrentCamera = nil
    end
    if train.CurrentCamera ~= oldCam then
        if not train.CurrentCamera then train.CurrentCamera = 0 end
        if train.CurrentCamera > 0 then
            local camera = train.Cameras[train.CurrentCamera]
            local seatAng = ply:GetVehicle():GetAngles()
            LocalPlayer():SetEyeAngles(train:LocalToWorldAngles(camera[2]-seatAng))
        else
            LocalPlayer():SetEyeAngles(Angle(0,ply:GetVehicle():GetModel()=="models/nova/jeep_seat.mdl" and 90 or 0,0))
        end
        MsgC("Curent camera:",Color(255,0,0),train.CurrentCamera,"\n")
        if train.CamMoved then train:CamMoved() end
        lastChanged = RealTime()
        if camEnd == 1 then camStart = 0 end
        camEnd = 0
    end
end
hook.Add("PlayerButtonDown", "metrostroi-cabin-buttons", function(ply,key) handleKeyEvent(ply, key,true) handleCam(ply,key) end)
hook.Add("PlayerButtonUp", "metrostroi-cabin-buttons", function(ply,key) handleKeyEvent(ply, key,false) end)
if game.SinglePlayer() then
    net.Receive("PlayerButtonDown_metrostroi",function()
        local key = net.ReadUInt(16)
        handleCam(LocalPlayer(),key)
        handleKeyEvent(LocalPlayer(),key,true)
    end)
    net.Receive("PlayerButtonUp_metrostroi",function()
        handleKeyEvent(LocalPlayer(),net.ReadUInt(16),false)
    end)
end
local Gradient = Material("vgui/gradient-d")
local oldTrain
hook.Add( "HUDPaint", "metrostroi-draw-cameras", function()
    local train, outside = isValidTrainDriver(LocalPlayer())
    if not IsValid(train) or not train.Cameras or outside then
        if IsValid(oldTrain) then
            oldTrain.CurrentCamera = 0
            if oldTrain.CamMoved then oldTrain:CamMoved() end
            oldTrain = nil
            camStart = 0
            camEnd = 1
        end
        return
    end
    oldTrain = train
    local cam = train.Cameras[train.CurrentCamera]
    camAnim = camAnim+(train.CurrentCamera-camAnim)*FrameTime()*5
    if camStart < 1 then camStart = math.Clamp(camStart+FrameTime()*2,0,1) end
    if RealTime()-lastChanged > 5 and camEnd < 1 then camEnd = math.Clamp(camEnd+FrameTime()*0.5,0,1) end
    local a = math.Clamp(camStart*(1-camEnd)*255,0,255 )-- + math.Clamp(255-(255-RealTime()-lastChanged+4)*512,0,255 )
    if a<= 0 then return end
    surface.SetDrawColor(255,255,255,a)
    surface.DrawRect(15,40,384,40)
    render.SetScissorRect( 0, 60-15, 512, 60+15, true ) -- Enable the rect
        draw.SimpleText(Metrostroi.GetPhrase("Train.Common.Camera0"),"Trebuchet24",20,60-camAnim*50,Color( 0, 0, 0,a),TEXT_ALIGN_LEFT,TEXT_ALIGN_CENTER)
        for k,v in ipairs(train.Cameras) do
            draw.SimpleText(Metrostroi.GetPhrase(v[3]),"Trebuchet24",20,60+(k-camAnim)*50,Color( 0, 0, 0,a),TEXT_ALIGN_LEFT,TEXT_ALIGN_CENTER)
        end
    render.SetScissorRect( 0, 0, 0, 0, false ) -- Disable after you are done
                --[[
    render.SetStencilEnable(true)
    render.SetStencilTestMask(255);render.SetStencilWriteMask(255);render.SetStencilReferenceValue(10)
    render.SetStencilPassOperation(STENCIL_REPLACE)
    render.SetStencilFailOperation(STENCIL_KEEP)
    render.SetStencilZFailOperation(STENCIL_KEEP)
    render.SetStencilCompareFunction(STENCIL_ALWAYS)

    render.SetStencilCompareFunction(STENCIL_EQUAL)

    render.SetStencilEnable(false)
    render.SetScissorRect(0,0,0,0,false)]]
end)
local ppMat = Material("pp/blurx")
hook.Add( "HUDPaint", "metrostroi-draw-crosshair-tooltip", function()
    --if not drawCrosshair then return end
    if IsValid(LocalPlayer()) then
        local scrX,scrY = ScrW(),ScrH()

        if canDrawCrosshair then
            surface.DrawCircle(scrX/2,scrY/2,4.1,drawCrosshair and Color(255,0,0) or Color(255,255,150))
        end

        if toolTipText ~= nil then
            surface.SetFont("MetrostroiLabels")
            local w,h = surface.GetTextSize("SomeText")
            local height = h*1.1
            local texts = string.Explode("\n",toolTipText)
            surface.SetDrawColor(0,0,0,125)
            for i,v in ipairs(texts) do
                local y = scrY/2+height*(i)
                if #v==0 then continue end
                local w2,h2 = surface.GetTextSize(v)
                surface.DrawRect(scrX/2-w2/2-5, scrY/2-h2/2+height*(i), w2+10, h2)
                --[[if toolTipPosition and i==#texts then
                    local st,en = v:find(toolTipPosition)
                    local textSt,textEn = v:sub(1,st-1),v:sub(en+1,-1)
                    local x1 = 0-w2/2
                    local x2 = surface.GetTextSize(textSt)-w2/2
                    local x3 = surface.GetTextSize(textSt)+surface.GetTextSize(toolTipPosition)-w2/2
                    draw.SimpleText(textSt,"MetrostroiLabels",scrX/2+x1,y, toolTipColor or Color(255,255,255),TEXT_ALIGN_LEFT,TEXT_ALIGN_CENTER)
                    draw.SimpleText(toolTipPosition,"MetrostroiLabels",scrX/2+x2,y, toolTipColor or Color(0,255,0),TEXT_ALIGN_LEFT,TEXT_ALIGN_CENTER)
                    draw.SimpleText(textEn,"MetrostroiLabels",scrX/2+x3,y, toolTipColor or Color(255,255,255),TEXT_ALIGN_LEFT,TEXT_ALIGN_CENTER)
                    Metrostroi.DrawLine(scrX/2+x2,y+h/2-3,scrX/2+x3,y+h/2-3,toolTipColor or Color(0,255,0),1)
                else]]
                    draw.SimpleText(v,"MetrostroiLabels",scrX/2,y, toolTipColor or Color(255,255,255),TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
                --end
            end
            --[[
            local w1 = surface.GetTextSize(text1)
            local w2 = surface.GetTextSize(text2)

            surface.SetTextColor(toolTipColor or Color(255,255,255))
            surface.SetTextPos((scrX-w1)/2,scrY/2+10)
            surface.DrawText(text1)
            surface.SetTextPos((scrX-w2)/2,scrY/2+30)
            surface.DrawText(text2)]]
        end
    end
end)

language.Add("SBoxLimit_train_limit","Wagons limit")
--------------------------------------------------------------------------------
-- Turn light on or off
--------------------------------------------------------------------------------
function ENT:SetLightPower(index,power,brightness)
    if self.HiddenLamps[index] then return end
    local lightData = self.LightsOverride[index] or self.Lights[index]
    brightness = brightness or 1
    if lightData[1] == "glow" or lightData[1] == "light" then
        if lightData.panel and not self.SpritesEnabled or lightData.aa and self.AAEnabled then return end
        self.LightBrightness[index] = brightness * (lightData.brightness or 0.5)
        if power and self.Sprites[index] then return end
        self.Sprites[index] = nil
        if not power then return end
        self.Sprites[index] = util.GetPixelVisibleHandle()
        lightData.mat = Metrostroi.MakeSpriteTexture((lightData.texture or "sprites/light_glow02"),lightData[1] == "light")
        return
    end

    if power and IsValid(self.GlowingLights[index]) then
        if lightData[1] == "headlight" and IsValid(self.GlowingLights[index]) then
            -- Check if light already glowing
            if brightness ~= self.LightBrightness[index] then
                local light = self.GlowingLights[index]
                light:SetBrightness(brightness * (lightData.brightness or 1.25))
                light:Update()
                self.LightBrightness[index] = brightness
            end
            return
        elseif (lightData[1] == "glow") or (lightData[1] == "light") then
            local brightness = brightness * (lightData.brightness or 0.5)
            if brightness ~= self.LightBrightness[index] then
                local light = self.GlowingLights[index]
                light:SetBrightness(brightness)
                self.LightBrightness[index] = brightness
            end
            return
        elseif lightData[1] == "dynamiclight" then
            if brightness ~= self.LightBrightness[index] then
                local light = self.GlowingLights[index]
                light:SetLightStrength(brightness)
                self.LightBrightness[index] = brightness
            end
            return
        end
    end
    if IsValid(self.GlowingLights[index]) then
        self.GlowingLights[index]:Remove()
    end
    self.GlowingLights[index] = nil
    self.LightBrightness[index] = brightness
    if not power then return end
    -- Create light
    if lightData[1] == "light" or lightData[1] == "glow" then
        local light = ents.CreateClientside("gmod_train_sprite")
        light:SetPos(self:LocalToWorld(lightData[2]))
        --light:SetLocalAngles(lightData[3])

        -- Set parameters
        local brightness = brightness * (lightData.brightness or 0.5)
        light:SetColor(lightData[4])
        light:SetBrightness(brightness)
        light:SetTexture((lightData.texture or "sprites/light_glow02")..".vmt",lightData[1] == "light")
        light:SetSize(lightData.scale or 1.0)
        light:Set3D(false)

        self.GlowingLights[index] = light
    elseif (lightData[1] == "headlight") and (not lightData.backlight or self.RedLights) and (not lightData.panellight or self.OtherLights) then
        local light = ProjectedTexture()
        light:SetPos(self:LocalToWorld(lightData[2]))
        light:SetAngles(self:LocalToWorldAngles(lightData[3]))
        --light:SetParent(self)
        --light:SetLocalPos(lightData[2])
        --light:SetLocalAngles(lightData[3])

        -- Set parameters
        if lightData.headlight and self.HeadlightShadows or not lightData.headlight and self.OtherShadows then
            light:SetEnableShadows((lightData.shadows or 0)>0)
        else
            light:SetEnableShadows(false)
        end
        if (lightData.shadows or 0)>0 then
            light:SetFarZ(math.max(lightData.farz or 2048,10))
        else
            light:SetFarZ(lightData.farz or 2048)
        end
        light:SetNearZ(lightData.nearz or 16)
        if lightData.fov then light:SetFOV(lightData.fov or 120) end
        if lightData.hfov then light:SetHorizontalFOV(lightData.hfov) end
        if lightData.vfov then light:SetVerticalFOV(lightData.vfov or 120) end
        light:SetOrthographic(false)
        -- Set Brightness
        light:SetBrightness(brightness * (lightData.brightness or 1.25))
        light:SetColor(lightData[4])
        light:SetTexture(lightData.texture or "effects/flashlight001")

        -- Turn light on
        light:Update() --"effects/flashlight/caustics"
        self.GlowingLights[index] = light
    elseif lightData[1] == "dynamiclight" then
        local light = ents.CreateClientside("gmod_train_dlight")
        light:SetParent(self)

        -- Set position
        light:SetLocalPos(lightData[2])
        --light:SetLocalAngles(lightData[3])

        -- Set parameters
        light:SetDColor(lightData[4])
        light:SetSize(lightData.distance)
        light:SetBrightness(lightData.brightness or 2)
        light:SetLightStrength(brightness)

        -- Turn light on
        light:Spawn()
        self.GlowingLights[index] = light
    end
end

function ENT:OnStyk(soundid,location,range,pitch)
    local speed = self:GetNW2Float("TrainSpeed",0)/100
    --local str = ""
    if self.TunnelCoeff > 0.01 then
        --local snd = Format("b%dtunnel_%d%s",pitch,range%10+1,soundid)
        --str=str..Format("tun: %s=%s vol=%d pitch=",snd,self.SoundNames[snd],self.TunnelCoeff*(0.9-math.min(speed,1)*0.3),0.9+math.min(speed,1)*0.3)
        self:PlayOnce(Format("b%dtunnel_%d%s",pitch,range%10+1,soundid),"bass",self.TunnelCoeff*(0.9-math.min(speed,1)*0.3),0.9+math.min(speed,1)*0.2)
    end
    if self.StreetCoeff > 0.01 then
        --local snd = Format("b%dstreet_%d%s",pitch,range%14+1,soundid)
        --str=str..Format(", str: %s=%s vol=%d pitch=",snd,self.SoundNames[snd],self.StreetCoeff*(0.6-math.min(speed,1)*0.3),0.9+math.min(speed,1)*0.3)
        self:PlayOnce(Format("b%dstreet_%d%s",pitch,range%14+1,soundid),"bass",self.StreetCoeff*(0.6-math.min(speed,1)*0.3),0.9+math.min(speed,1)*0.2)
    end
    --RunConsoleCommand("say",str)
end

concommand.Add("metrostroi_reload_client",function()
    Metrostroi.ReloadClientside = true
    timer.Simple(0.5,function() if Metrostroi.ReloadClientside then Metrostroi.ReloadClientside = false end end)
end,nil,"Reload all clientside models")
Metrostroi.OptimisationPatch()