--[[ | This file was obtained through the combined efforts | of Madbluntz & Plymouth Antiquarian Society. | | Credits: lifestorm, Gregory Wayne Rossel JR., | Maloy, DrPepper10 @ RIP, Atle! | | Visit for more: https://plymouth.thetwilightzone.ru/ --]] SWEP.Flashlights = {} -- tracks projectedlights -- {{att = int, light = ProjectedTexture}} SWEP.CheapFlashlights = {} -- tracks cheap flashlight models + lights -- {{att = int, dlight = DynamicLight, vlight = ClientsideModel}} function SWEP:GetHasFlashlights() for i, k in pairs(self.Attachments) do if !k.Installed then continue end if self:GetBuff_Stat("Flashlight", i) != nil then return true end end return false end function SWEP:CreateFlashlightsVM() self:KillFlashlights() self.Flashlights = {} local total_lights = 0 for i, k in pairs(self.Attachments) do if !k.Installed then continue end if self:GetBuff_Stat("Flashlight", i) then local newlight = { att = i, light = ProjectedTexture(), bone = self:GetBuff_Stat("FlashlightBone", i) or "laser", col = self:GetBuff_Stat("FlashlightColor", i) or Color(255, 255, 255), br = self:GetBuff_Stat("FlashlightBrightness", i) or 2 } total_lights = total_lights + 1 local l = newlight.light if !IsValid(l) then continue end table.insert(self.Flashlights, newlight) l:SetFOV(self:GetBuff_Stat("FlashlightFOV", i) or 50) if self:GetBuff_Stat("FlashlightHFOV", i) then l:SetHorizontalFOV(self:GetBuff_Stat("FlashlightHFOV", i)) end if self:GetBuff_Stat("FlashlightVFOV", i) then l:SetVerticalFOV(self:GetBuff_Stat("FlashlightVFOV", i)) end l:SetFarZ(self:GetBuff_Stat("FlashlightFarZ", i) or 512) l:SetNearZ(self:GetBuff_Stat("FlashlightNearZ", i) or 4) local atten = self:GetBuff_Stat("FlashlightAttenuationType", i) or ArcCW.FLASH_ATT_LINEAR l:SetLinearAttenuation(0) l:SetConstantAttenuation(0) l:SetQuadraticAttenuation(0) if atten == ArcCW.FLASH_ATT_CONSTANT then l:SetConstantAttenuation(100) elseif atten == ArcCW.FLASH_ATT_QUADRATIC then l:SetQuadraticAttenuation(100) else l:SetLinearAttenuation(100) end l:SetColor(self:GetBuff_Stat("FlashlightColor", i) or Color(255, 255, 255)) l:SetTexture(self:GetBuff_Stat("FlashlightTexture", i)) l:SetBrightness(self:GetBuff_Stat("FlashlightBrightness", i)) l:SetEnableShadows(true) l:Update() local g_light = { Weapon = self, ProjectedTexture = l } table.insert(ArcCW.FlashlightPile, g_light) end end if total_lights > 2 then -- you are a madman for i, k in pairs(self.Flashlights) do if k.light:IsValid() then k.light:SetEnableShadows(false) end end end end -- for world model flashlights we will use a cheap solution similar to what HL2 uses -- throw up a volumetric light model -- function SWEP:CreateFlashlightsWM() -- self:KillFlashlights() -- self.CheapFlashlights = {} -- for i, k in pairs(self.Attachments) do -- if !k.Installed then continue end -- local atttbl = ArcCW.AttachmentTable[k.Installed] -- if atttbl.Flashlight then -- local newlight = { -- att = i, -- vlight = ClientsideModel(ArcCW.VolumetricLightModel), -- scale_x = 1, -- scale_y = 1, -- maxz = atttbl.FlashlightFarZ or 512, -- bone = atttbl.FlashlightBone or "laser", -- col = Color(255, 255, 255) -- } -- local vl = newlight.vlight -- if !IsValid(vl) then continue end -- table.insert(self.CheapFlashlights, newlight) -- local xfov = atttbl.FlashlightHFOV or atttbl.FlashlightFOV or 50 -- local yfov = atttbl.FlashlightVFOV or atttbl.FlashlightFOV or 50 -- local target_x = 128 * (xfov / 90) -- local target_y = 128 * (yfov / 90) -- local scale_x = target_x / ArcCW.VolumetricLightX -- local scale_y = target_y / ArcCW.VolumetricLightY -- newlight.scale_x = scale_x -- newlight.scale_y = scale_y -- vl:SetNoDraw(ArcCW.NoDraw) -- vl:DrawShadow(false) -- local col = atttbl.FlashlightColor or Color(255, 255, 255) -- col = Color(255, 0, 0) -- newlight.col = col -- -- vl:SetColor(col) -- local g_light = { -- Model = vl, -- Weapon = self -- } -- table.insert(ArcCW.CSModelPile, g_light) -- end -- end -- end function SWEP:KillFlashlights() self:KillFlashlightsVM() -- self:KillFlashlightsWM() end function SWEP:KillFlashlightsVM() if !self.Flashlights then return end for i, k in pairs(self.Flashlights) do if k.light and k.light:IsValid() then k.light:Remove() end end self.Flashlights = nil end function SWEP:KillFlashlightsWM() -- if !self.CheapFlashlights then return end -- for i, k in pairs(self.CheapFlashlights) do -- if k.vlight and k.vlight:IsValid() then -- k.vlight:Remove() -- end -- end -- self.CheapFlashlights = nil end -- given fov and distance solve apparent size local function solvetriangle(angle, dist) local a = angle / 2 local b = dist return b * math.tan(a) * 2 end local flashlight_mat = Material("models/effects/vol_light002") -- local flashlight_mat = Material("effects/blueblacklargebeam") function SWEP:DrawFlashlightsWM() -- if !self.CheapFlashlights then -- self:CreateFlashlightsWM() -- end local owner = self:GetOwner() for i, k in pairs(self.Attachments) do if !k.Installed then continue end local atttbl = ArcCW.AttachmentTable[k.Installed] if !atttbl or !self:GetBuff_Stat("Flashlight", i) then continue end local maxz = atttbl.FlashlightFarZ or 512 local bone = atttbl.FlashlightBone or "laser" local col = atttbl.FlashlightColor or Color(255, 255, 255) if !k.WElement then continue end local model = k.WElement.Model if !IsValid(model) then return end local pos, ang, dir if !model then pos = owner:EyePos() ang = owner:EyeAngles() dir = ang:Forward() else local att = model:LookupAttachment(bone or "laser") att = att == 0 and model:LookupAttachment("muzzle") or att if att == 0 then pos = model:GetPos() ang = IsValid(owner) and owner:EyeAngles() or model:GetAngles() dir = ang:Forward() dir_2 = ang:Up() else local attdata = model:GetAttachment(att) pos, ang = attdata.Pos, attdata.Ang dir = -ang:Right() dir_2 = ang:Up() end end local maxs = Vector(2, 2, 2) local mins = -maxs -- scale volumetric light local tr = util.TraceHull({ start = pos, endpos = pos + (dir * maxz), mask = MASK_OPAQUE, mins = mins, maxs = maxs }) local z = (tr.HitPos - tr.StartPos):Length() -- local s_z = z / ArcCW.VolumetricLightZ local xfov = atttbl.FlashlightHFOV or atttbl.FlashlightFOV or 50 local yfov = atttbl.FlashlightVFOV or atttbl.FlashlightFOV or 50 -- local target_x = 128 * (xfov / 90) -- local target_y = 128 * (yfov / 90) local target_x = solvetriangle(xfov, z) local target_y = target_x if xfov != yfov then target_y = solvetriangle(yfov, z) end local vs = EyeAngles():Up() local c1 = pos + vs local c4 = pos - vs local c2 = tr.HitPos + (vs * target_y * 0.75) local c3 = tr.HitPos - (vs * target_y * 0.75) render.SetMaterial(flashlight_mat) render.DrawQuad(c1,c2,c3,c4, col) -- local scale = Matrix() -- scale:Scale(Vector(s_x, s_y, s_z)) -- k.vlight:SetPos(pos) -- k.vlight:SetAngles(ang + Angle(0, 0, 90)) -- k.vlight:EnableMatrix("RenderMultiply", scale) -- k.vlight:SetColor(Color(255, 0, 0, 255)) -- k.vlight:SetRenderMode(RENDERMODE_NORMAL) -- k.vlight:SetKeyValue("RenderFX", kRenderFxNone) -- k.vlight:DrawModel() -- place dynamic light to make some light appear local dl = DynamicLight(self:EntIndex()) local delta = (z / maxz) delta = math.Clamp(delta, 0, 1) if dl then dl.pos = tr.HitPos dl.r = col.r dl.g = col.g dl.b = col.b dl.brightness = Lerp(delta, atttbl.FlashlightBrightness or 2, 0) -- print(z / maxz) dl.Decay = 1000 / 1 dl.dietime = CurTime() + 0.1 dl.size = xfov * 5 end end end function SWEP:DrawFlashlightsVM() if !self.Flashlights then self:CreateFlashlightsVM() end local owner = self:GetOwner() for i, k in pairs(self.Flashlights) do local model = (self.Attachments[k.att].VElement or {}).Model local pos, ang if !model then pos = owner:EyePos() ang = owner:EyeAngles() else local att = model:LookupAttachment(k.bone or "laser") att = att == 0 and model:LookupAttachment("muzzle") or att if att == 0 then pos = model:GetPos() ang = owner:EyeAngles() else local attdata = model:GetAttachment(att) pos, ang = attdata.Pos, attdata.Ang end end local tr = util.TraceLine({ start = owner:EyePos(), endpos = owner:EyePos() - ang:Right() * 128, mask = MASK_OPAQUE, filter = LocalPlayer(), }) if tr.Fraction < 1 then -- We need to push the flashlight back local tr2 = util.TraceLine({ start = owner:EyePos(), endpos = owner:EyePos() + ang:Right() * 128, mask = MASK_OPAQUE, filter = LocalPlayer(), }) -- push it as back as the area behind us allows pos = pos + ang:Right() * 128 * math.min(1 - tr.Fraction, tr2.Fraction) end ang:RotateAroundAxis(ang:Up(), 90) k.light:SetPos(pos) k.light:SetAngles(ang) k.light:Update() -- local col = k.col -- local dl = DynamicLight(self:EntIndex()) -- if dl then -- dl.pos = pos -- dl.r = col.r -- dl.g = col.g -- dl.b = col.b -- dl.brightness = k.br or 2 -- -- print(z / maxz) -- dl.Decay = 1000 / 0.1 -- dl.dietime = CurTime() + 0.1 -- dl.size = (k.br or 2) * 64 -- end end end