1
0
mirror of https://github.com/metrostroi-repo/MetrostroiAddon.git synced 2026-05-02 00:42:29 +00:00
Files
MetrostroiAddon/lua/entities/gmod_subway_base/cl_init.lua
2021-01-02 15:32:05 +03:00

2486 lines
91 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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_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 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)
hook.Add("Think","MetrostroiHUDHider",function()
local ply = LocalPlayer()
local seat = ply:GetVehicle()
if IsValid(seat) and IsValid(seat:GetNW2Entity("TrainEntity")) then
ply.InMetrostroiTrain = seat:GetNW2Entity("TrainEntity")
else
ply.InMetrostroiTrain = 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(v)
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)
local v = self.ClientPropsOv and self.ClientPropsOv[k] or self.ClientProps[k]
if v and k ~= "BaseClass" and not IsValid(self.ClientEnts[k]) and not self.Hidden[k] and not self.Hidden.anim[id] and (override or self:ShouldDrawClientEnt(self.ClientProps[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))
--[[
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
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 = {}
hook.Add("Think",cent,function(ent)
for k,v in pairs(ent.BASSSounds) do
if not IsValid(v) or v:GetState() == GMOD_CHANNEL_STOPPED then
table.remove(ent.BASSSounds,k)
end
end
ent:NextThink(RealTime()+0.5)
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
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.Add("EntityRemoved","metrostroi_bass_disable",function(ent)
if ent.BASSSounds then
for k,v in pairs(ent.BASSSounds) do v:Stop() 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()
hook.Add("Think","SpwanElasped",function() elasped = SysTime() end)
function ENT:CreateCSEnts()
local count = 0
local time = C_SoftDraw:GetFloat()/100*0.001
if self.ClientPropsOv then
for k in pairs(self.ClientPropsOv) do
if k ~= "BaseClass" and not IsValid(self.ClientEnts[k]) then
self:SpawnCSEnt(k)
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 count > 5 and SysTime()-elapsed > time then return false end
if self:SpawnCSEnt(k) then count = count + 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 = {}
end
-- Checks if the player is driving a train, also returns said train
local function isValidTrainDriver(ply)
local train
local seat = ply:GetVehicle()
if IsValid(seat) then train = seat:GetNW2Entity("TrainEntity") end
if IsValid(train) then return train end
local weapon = IsValid(LocalPlayer():GetActiveWeapon()) and LocalPlayer():GetActiveWeapon():GetClass()
if weapon == "train_kv_wrench" or weapon == "train_kv_wrench_gold" then
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
end
return train, true
end
--------------------------------------------------------------------------------
-- Clientside initialization
--------------------------------------------------------------------------------
function ENT:CanDrawThings()
return not IsValid(LocalPlayer():GetVehicle()) or self == LocalPlayer():GetVehicle():GetParent()
end
hook.Add("PostDrawTranslucentRenderables", "metrostroi_base_draw", function(_,isDD)
if isDD then return end
for _,ent in pairs(ents.GetAll()) do
--print(self.BaseClassName)
if ent.Base ~= "gmod_subway_base" then continue end
if MetrostroiStarted~=true then
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(MetrostroiStarted and 10-(RealTime()-MetrostroiStarted) or 10))
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(MetrostroiStarted and 10-(RealTime()-MetrostroiStarted) or 10))
cam.End3D2D()
end
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
--[[
--Drawing schedule for trains which support it
if ent.ButtonMap and ent.ButtonMap["Schedule"] then
ent:DrawOnPanel("Schedule", function(panel)
ent:DrawSchedule(panel)
end)
end]]
-- Debug draw for buttons
if C_DrawDebug:GetInt() > 0 and (ent.ButtonMap ~= nil) then
draw.NoTexture()
for kp,panel in pairs(ent.ButtonMap) do
if kp ~= "BaseClass" 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)
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.Hidden = {
anim = {},button = {},override = {},
}
self.Anims = {}
self.SmoothHide = {}
-- Create sounds
self:InitializeSounds()
recurePrecache(self.SoundNames)
self.Sounds = {
loop = {}
}
self.CurrentCamera = 0
if self.NoTrain then return end
self.ButtonMapMatrix = {}
-- Passenger models
self.PassengerEnts = {}
self.PassengerPositions = {}
--self.HiddenQuele = {}
-- Systems defined in the train
self.Systems = {}
-- Initialize train systems
self:InitializeSystems()
--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)
if not nfinal then
self.RenderBlock = RealTime()
else
drawCrosshair = false
canDrawCrosshair = false
toolTipText = nil
end
self:RemoveCSEnts()
self.RenderClientEnts = false
for _,v in pairs(self.Sounds) do
if type(v) ~= "function" and IsValid(v) then
v:Stop()
end
end
for k,v in pairs(self.Sounds.loop) do
for i,sndt in ipairs(v) do
if IsValid(sndt.sound) then
sndt.sound:Stop()
end
end
end
for _,v in pairs(self.PassengerEnts or {}) do
SafeRemoveEntity(v)
end
if self.GUILocker then self:BlockInput(false) end
self.Sounds = {loop = {}}
self.PassengerEnts = {}
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
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
if v.Remove then v:Remove() end
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
--self.Sounds[k] = nil
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
local _TEST
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==true then
hook.Remove("Think","MetrostroiStarted")
elseif MetrostroiStarted == nil then
MetrostroiStarted = RealTime()
elseif MetrostroiStarted and RealTime()-MetrostroiStarted > 10 then
MetrostroiStarted = true
end
if IsValid(LocalPlayer()) and not _TEST then
_TEST = true
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 self.RenderBlock then
if RealTime()-self.RenderBlock < 5 then
self.ClientPropsInitialized = false
return
else
self.RenderBlock = false
end
end
if MetrostroiStarted~=true then
return
end
if not self.FirstTick then
self.FirstTick = true
self.RenderClientEnts = true
self.CreatingCSEnts = false
return
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()) then
self.HeadlightShadows = C_Shadows1:GetBool()
self.OtherShadows = C_Shadows2:GetBool()
for k,v in pairs(self.GlowingLights) do
if IsValid(v) then v:Remove() end
end
self.GlowingLights = {}
self.LightBrightness = {}
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)
for k,v in pairs(self.PassengerEnts or {}) do
SafeRemoveEntity(v)
end
self.PassengerEnts = {}
return
end
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 (GetConVarNumber("metrostroi_disablecamaccel") == 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
for k,v in pairs(self.Sounds.loop) do
local tbl = self.SoundPositions[k]
local ntbl = self.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
if tbl then
--snd:SetPos(self:LocalToWorld(tbl[3]))
else
--snd:SetPos(self:GetPos())
end
--snd:SetPlaybackRate(v.pitch)
--snd:SetVolume(stbl.volume or 0)
self:SetPitchVolume(snd,v.pitch or 1,stbl.volume,tbl)
--snd:Set3DFadeDistance(tbl[1],tbl[2])
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:SetTime(0)
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]:SetTime(0)
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:SetTime(0)
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:SetTime(0)
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]:SetTime(0)
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
local seat = LocalPlayer():GetVehicle()
if IsValid(seat) and self == seat:GetNW2Entity("TrainEntity") 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()
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(v)
if IsValid(cent) and hidden then
cent:Remove()
self.ClientEnts[k] = nil
elseif not IsValid(cent) and not hidden then
self:SpawnCSEnt(k)
end
end
for k,v in pairs(self.ButtonMap) do
if not v.pos then continue end
local cent = self.ClientEnts[k]
if not v.hide or (v.nohide or screenshotMode) then
self.HiddenPanelsDistance[k] = v.screenHide
continue
end
self.HiddenPanelsDistance[k] = not self:ShouldDrawClientEnt(v)
end
end
if self.AutoAnims then
for name,func in ipairs(self.AutoAnims) do
func(self)
end
end
if self.Lights and self.GlowingLights then
for id, light in pairs(self.GlowingLights) do
light:Update()
end
end
-- Update passengers
if self.RenderClientEnts and self.PassengerEnts then
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
elseif self.PassengerEnts then
for k,v in pairs(self.PassengerEnts) do
if IsValid(v) then v:Remove() end
self.PassengerEnts[k] = nil
end
end
end
function ENT:BlockInput(block)
local seat = LocalPlayer():GetVehicle()
if IsValid(seat) then
if self ~= seat:GetNW2Entity("TrainEntity") 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 OldTrainHandle
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
OldTrainHandle = train
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)
--[[ 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
if not self.Anims[id] then
self.Anims[id] = {}
self.Anims[id].val = value
self.Anims[id].value = min + (max-min)*value
self.Anims[id].V = 0.0
self.Anims[id].block = false
self.Anims[id].stuck = false
self.Anims[id].P = value
end
if self.Hidden[id] or self.Hidden.anim[id] then return 0 end
if self.Anims[id].Ignore then
if RealTime()-self.Anims[id].Ignore < 0 then
return self.Anims[id].value
else
self.Anims[id].Ignore = nil
end
end
local val = self.Anims[id].val
if value ~= val then
self.Anims[id].block = false
end
if self.Anims[id].block then
if self.Anims[id].reload and IsValid(self.ClientEnts[clientProp]) then
self.ClientEnts[clientProp]:SetPoseParameter("position",self.Anims[id].value)
self.Anims[id].reload = false
end
return self.Anims[id].value--min + (max-min)*self.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(self.Anims[id].P - value) < stickyness) and (self.Anims[id].stuck) then
value = self.Anims[id].P
self.Anims[id].stuck = false
else
self.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
self.Anims[id].V = 0
else
self.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) - self.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
self.Anims[id].V = self.Anims[id].V + dX2dT * dT
if self.Anims[id].V > max_speed then self.Anims[id].V = max_speed end
if self.Anims[id].V < -max_speed then self.Anims[id].V = -max_speed end
val = math.max(0,math.min(1,val + self.Anims[id].V * dT))
-- Check if value got stuck
if (math.abs(dX2dT) < 0.001) and stickyness and (dT > 0) then
self.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(self.Anims[id].V) == 0 and math.abs(val-value) == 0 and not self.Anims[id].stuck then
self.Anims[id].block = true
end
self.Anims[id].val = val
self.Anims[id].oldival = value
self.Anims[id].oldspeed = speed
self.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]) then
self:SpawnCSEnt(clientProp)
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 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("CalcView", "Metrostroi_TrainView", function(ply,pos,ang,fov,znear,zfar)
local seat = ply: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 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() then
local trainAng = ang - 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
local target_ang = (train:GetAngles() + Angle(2,0,0))
target_ang:RotateAroundAxis(train:GetAngles():Up(),180)
train.CamPos = train:LocalToWorld(Vector(475,80,30))
train.CamAngles = target_ang
--[[ if ply.OldFOV ~= 30 then
ply:SetFOV(30,0)
ply.OldFOV = 30
end--]]
return {
origin = train.CamPos,
angles = target_ang,
fov = 30,
znear = znear,
zfar = zfar
}
else
local target_ang = (train:GetAngles() + Angle(2,0,0))
target_ang:RotateAroundAxis(train:GetAngles():Up(),180)
train.CamPos = train:LocalToWorld(Vector(475,-80,30))
train.CamAngles = target_ang
--[[ if ply.OldFOV ~= 20 then
ply:SetFOV(20,0)
ply.OldFOV = 20
end--]]
return {
origin = train.CamPos,
angles = target_ang,
fov = 20,
znear = znear,
zfar = zfar
}
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 = fov/C_FovDesired:GetFloat()*C_CabFOV:GetFloat()
--ply:SetFOV(tFov,0)
return {
origin = train.CamPos,
angles = ang,--+train:LocalToWorldAngles(camera[2]),
fov = tFov,
znear = znear,
zfar = zfar
}
else
--train.HeadAcceleration = GetConVarNumber("metrostroi_disablecamaccel") == 0 and math.Clamp((train.HeadAcceleration or 0)*0.95 + (train:GetNW2Float("Accel"))*1.1, -10, 10) or 0
--function ENT:Animate(clientProp, value, min, max, speed, damping, stickyness)
--print(train:GetNW2Float("Accel")*1.1,train:Animate("accel",train:GetNW2Float("Accel")*1.1+5,-0,10, nil, nil, 64,2,4))
--local headPos = train:WorldToLocal(pos)
train.CamPos = train:LocalToWorld(train:WorldToLocal(pos)+Vector(train.HeadAcceleration,0,0))
local tFov = fov/C_FovDesired:GetFloat()*C_CabFOV:GetFloat()
--ply:SetFOV(tFov,0)
train.CamAngles = ang
return {
origin = train.CamPos,
angles = ang,
fov = tFov,
znear = znear,
zfar = zfar
}
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 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 = GetConVarNumber("metrostroi_tooltip_delay")
if GetConVarNumber("metrostroi_disablehovertext") == 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
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 vgui.GetHoveredPanel() and vgui.GetHoveredPanel():GetName() ~= "ContextMenu" and vgui.GetHoveredPanel():GetName() ~= "GModBase"--]] 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 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 = surface.ScreenWidth(),surface.ScreenHeight()
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
local text1 = string.sub(toolTipText,1,string.find(toolTipText,"\n"))
local text2 = string.sub(toolTipText,string.find(toolTipText,"\n") or 1e9)
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
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)
draw.SimpleText(v,"MetrostroiLabels",scrX/2,scrY/2+height*(i), toolTipColor or Color(255,255,255),TEXT_ALIGN_CENTER,TEXT_ALIGN_CENTER)
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)
local lightData = self.Lights[index]
self.GlowingLights = self.GlowingLights or {}
self.LightBrightness = self.LightBrightness or {}
brightness = brightness or 1
if (lightData[1] == "headlight") then
if (power and (not lightData.backlight or C_Shadows3:GetBool()) 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
end
-- Turn off light
if IsValid(self.GlowingLights[index]) then
self.GlowingLights[index]:Remove()
end
end
self.GlowingLights[index] = nil
self.LightBrightness[index] = brightness
-- Create light
if (lightData[1] == "headlight") and (power and (not lightData.backlight or C_Shadows3:GetBool())) 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
--light:SetKeyValue("enableshadows", lightData.shadows or 1)
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
--light:SetKeyValue("farz", lightData.farz or 2048)
light:SetFarZ(lightData.farz or 2048)
--light:SetKeyValue("nearz", lightData.nearz or 16)
light:SetNearZ(lightData.nearz or 16)
--light:SetKeyValue("lightfov", lightData.fov or 120)
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
--local brightness = brightness * (lightData.brightness or 1.25)
light:SetBrightness(brightness * (lightData.brightness or 1.25))
--[[light:SetKeyValue("lightcolor",
Format("%i %i %i 255",
lightData[4].r*brightness,
lightData[4].g*brightness,
lightData[4].b*brightness
)
)]]
light:SetColor(lightData[4])
light:SetTexture(lightData.texture or "effects/flashlight001")
-- Turn light on
light:Update() --"effects/flashlight/caustics"
--light:Input("SpotlightTexture",nil,nil,lightData.texture or "effects/flashlight001")
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")