mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
Upload
This commit is contained in:
231
lua/weapons/sf2_tool/cl_init.lua
Normal file
231
lua/weapons/sf2_tool/cl_init.lua
Normal file
@@ -0,0 +1,231 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Point and click
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
include("shared.lua")
|
||||
|
||||
|
||||
if ( SERVER ) then
|
||||
SWEP.AutoSwitchTo = false
|
||||
SWEP.AutoSwitchFrom = false
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
if not game.SinglePlayer() and not IsFirstTimePredicted() then return end
|
||||
local tool = self:GetTool()
|
||||
if not tool or not tool.LeftClick then return end
|
||||
tool.LeftClick(tool, self:GetOwner():GetEyeTrace())
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if not game.SinglePlayer() and not IsFirstTimePredicted() then return end
|
||||
local tool = self:GetTool()
|
||||
if not tool or not tool.RightClick then return end
|
||||
tool.RightClick(tool, self:GetOwner():GetEyeTrace())
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
self:RemoveGhost()
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:OnRemove()
|
||||
self:RemoveGhost()
|
||||
return true
|
||||
end
|
||||
|
||||
local oldTool = -1
|
||||
function SWEP:Think()
|
||||
local tool_id = self:GetToolID()
|
||||
if tool_id ~= oldTool then
|
||||
self:RemoveGhost()
|
||||
oldTool = tool_id
|
||||
self:SetTool(tool_id)
|
||||
end
|
||||
end
|
||||
|
||||
local ghostHalo
|
||||
function SWEP:SetGhost(mdl, pos, ang)
|
||||
-- Remove ghost if nil mdl
|
||||
if not mdl then
|
||||
if self._ghost and IsValid(self._ghost) then
|
||||
self._ghost:Remove()
|
||||
self._ghost = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
-- Make ghost or set mdl
|
||||
if not self._ghost or not IsValid(self._ghost) then
|
||||
self._ghost = ClientsideModel(mdl, RENDERMODE_TRANSCOLOR )
|
||||
ghostHalo = nil
|
||||
elseif self._ghost:GetModel() ~= mdl then
|
||||
self._ghost:SetModel(mdl)
|
||||
end
|
||||
-- Move ghost
|
||||
if pos then
|
||||
self._ghost:SetPos(pos)
|
||||
end
|
||||
if ang then
|
||||
self._ghost:SetAngles(ang)
|
||||
end
|
||||
return self._ghost
|
||||
end
|
||||
|
||||
function SWEP:SetGhostHalo(col)
|
||||
ghostHalo = col
|
||||
end
|
||||
|
||||
function SWEP:RemoveGhost()
|
||||
self:SetGhost()
|
||||
end
|
||||
|
||||
-- Context menu
|
||||
do
|
||||
local v = false
|
||||
hook.Add("OnContextMenuOpen", "StormFox2.Tool.COpen", function()
|
||||
v = true
|
||||
end)
|
||||
hook.Add("OnContextMenuClose", "StormFox2.Tool.CClose", function()
|
||||
v = false
|
||||
end)
|
||||
|
||||
function SWEP:IsContextMenuOpen()
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("PreDrawHalos", "StormFox2.GhostHalo", function()
|
||||
local wep = LocalPlayer():GetActiveWeapon()
|
||||
if not wep or not IsValid(wep) then return end
|
||||
if wep:GetClass() ~= "sf2_tool" then return end
|
||||
if not IsValid(wep._ghost) then return end
|
||||
if not ghostHalo then return end
|
||||
halo.Add( {wep._ghost}, ghostHalo, 5, 5, 2 )
|
||||
end)
|
||||
|
||||
SWEP.WepSelectIcon = surface.GetTextureID( "vgui/gmod_camera" )
|
||||
|
||||
-- Don't draw the weapon info on the weapon selection thing
|
||||
function SWEP:DrawHUD() end
|
||||
function SWEP:PrintWeaponInfo( x, y, alpha ) end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
function SWEP:CalcView(ply,pos,ang,fov)
|
||||
--pos = pos + ang:Forward() * -50
|
||||
return pos,ang,fov
|
||||
end
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
-- Unstable screen
|
||||
function SWEP:_GetScreenUN()
|
||||
return self._unstable or 0.2
|
||||
end
|
||||
function SWEP:_SetScreenUN( n )
|
||||
self._unstable = n
|
||||
end
|
||||
|
||||
-- Render screen
|
||||
local matScreen = Material( "stormfox2/weapons/sf_tool_screen" )
|
||||
local bgMat = Material("stormfox2/logo.png")
|
||||
local sMat = Material("effects/tvscreen_noise002a")
|
||||
local rMat = Material("gui/r.png")
|
||||
do
|
||||
local ScreenSize = 256
|
||||
local RTTexture = GetRenderTarget( "SFToolgunScreen", ScreenSize, ScreenSize )
|
||||
function SWEP:RenderToolScreen()
|
||||
local TEX_SIZE = ScreenSize
|
||||
-- Set up our view for drawing to the texture
|
||||
--cam.IgnoreZ(true)
|
||||
render.PushRenderTarget( RTTexture )
|
||||
render.ClearDepth()
|
||||
render.Clear( 0, 0, 0, 0 )
|
||||
cam.Start2D()
|
||||
-- Draw Screen
|
||||
local tool = self:GetTool()
|
||||
if not tool then
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(bgMat)
|
||||
surface.DrawTexturedRect(TEX_SIZE * 0.1,TEX_SIZE * 0.1,TEX_SIZE * 0.8,TEX_SIZE * 0.8)
|
||||
surface.SetMaterial(rMat)
|
||||
if math.Round(CurTime()%2) ~= 0 then
|
||||
surface.DrawTexturedRect(20,TEX_SIZE - 60,40,40)
|
||||
end
|
||||
else
|
||||
if not tool.NoPrintName then
|
||||
draw.DrawText(tool.PrintName or "Unknown", "sf_tool_large", TEX_SIZE / 2, 10, color_white, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
if tool.ScreenRender then
|
||||
tool:ScreenRender( TEX_SIZE, TEX_SIZE )
|
||||
end
|
||||
end
|
||||
-- surface.SetMaterial(sMat)
|
||||
-- surface.DrawTexturedRect(TEX_SIZE * 0.1,TEX_SIZE * 0.1,TEX_SIZE * 0.8,TEX_SIZE * 0.8)
|
||||
if self:_GetScreenUN() > 0.15 then
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(sMat)
|
||||
surface.DrawTexturedRect(0,0,TEX_SIZE * 2,TEX_SIZE * 2)
|
||||
end
|
||||
cam.End2D()
|
||||
render.PopRenderTarget()
|
||||
|
||||
matScreen:SetTexture( "$basetexture", RTTexture )
|
||||
matScreen:SetFloat("$shake", self:_GetScreenUN())
|
||||
--cam.IgnoreZ(false)
|
||||
end
|
||||
end
|
||||
|
||||
local mTool = Material("stormfox2/weapons/sf_tool")
|
||||
|
||||
function SWEP:PreDrawViewModel()
|
||||
if self:_GetScreenUN() > 0 then
|
||||
self:_SetScreenUN( math.max(0, self:_GetScreenUN() - FrameTime() * 0.6) )
|
||||
end
|
||||
self:RenderToolScreen()
|
||||
render.MaterialOverrideByIndex(1,matScreen)
|
||||
render.MaterialOverrideByIndex(2,mTool)
|
||||
|
||||
end
|
||||
function SWEP:PostDrawViewModel()
|
||||
render.MaterialOverrideByIndex()
|
||||
local tool = self:GetTool()
|
||||
if not tool then return end
|
||||
-- Render
|
||||
if tool.Render then
|
||||
cam.Start3D()
|
||||
tool:Render()
|
||||
cam.End3D()
|
||||
end
|
||||
end
|
||||
|
||||
-- CL swep rendering
|
||||
function SWEP:CalcViewModelView( vm, _,_,pos, ang)
|
||||
end
|
||||
|
||||
function SWEP:Deploy()
|
||||
self:_SetScreenUN( 0.2 )
|
||||
end
|
||||
|
||||
function SWEP:DrawWorldModel()
|
||||
local Owner = self:GetOwner()
|
||||
if IsValid(Owner) and Owner ~= LocalPlayer() then
|
||||
self:_SetScreenUN( 0 )
|
||||
elseif self:_GetScreenUN() < 0.4 then
|
||||
self:_SetScreenUN( math.min(0.4, self:_GetScreenUN() + FrameTime() * 0.2) )
|
||||
end
|
||||
self:RenderToolScreen()
|
||||
render.MaterialOverrideByIndex(1,matScreen)
|
||||
render.MaterialOverrideByIndex(2,mTool)
|
||||
self:DrawModel()
|
||||
render.MaterialOverrideByIndex()
|
||||
end
|
||||
103
lua/weapons/sf2_tool/init.lua
Normal file
103
lua/weapons/sf2_tool/init.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Point and click
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
AddCSLuaFile( "cl_init.lua" )
|
||||
AddCSLuaFile( "shared.lua" )
|
||||
|
||||
include("shared.lua")
|
||||
|
||||
SWEP.AutoSwitchTo = true
|
||||
SWEP.AutoSwitchFrom = true
|
||||
|
||||
function SWEP:ShouldDropOnDie() return false end
|
||||
|
||||
-- Add tools
|
||||
local TOOLS = {}
|
||||
|
||||
function SWEP:SwitchTool()
|
||||
local n = self:GetToolID() + 1
|
||||
if n > #self.Tool then
|
||||
n = 1
|
||||
end
|
||||
self:SetTool( n )
|
||||
end
|
||||
|
||||
function SWEP:HasAccessToSettings( onSuccess, ... )
|
||||
local a = {...}
|
||||
local ply = self:GetOwner()
|
||||
if not IsValid(ply) then return end
|
||||
CAMI.PlayerHasAccess(ply,"StormFox Settings",function(b)
|
||||
if not b then
|
||||
if IsValid(ply) then
|
||||
ply:EmitSound("ambient/alarms/klaxon1.wav")
|
||||
end
|
||||
SafeRemoveEntity(self)
|
||||
end
|
||||
onSuccess( unpack( a ) )
|
||||
end)
|
||||
end
|
||||
|
||||
function SWEP:Equip( newOwner )
|
||||
if newOwner:GetClass() ~= "player" then
|
||||
SafeRemoveEntity(self)
|
||||
else
|
||||
self:HasAccessToSettings( function() end )
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:PrimaryAttack()
|
||||
if not IsFirstTimePredicted() then return end
|
||||
if ( game.SinglePlayer() ) then self:CallOnClient( "PrimaryAttack" ) end
|
||||
local tool = self:GetTool()
|
||||
if not tool or not tool.LeftClick then return end
|
||||
local Owner = self:GetOwner()
|
||||
if tool.LeftClick(self, Owner:GetEyeTrace()) then
|
||||
self:DoShootEffect(Owner:GetEyeTrace(),IsFirstTimePredicted())
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:SecondaryAttack()
|
||||
if not IsFirstTimePredicted() then return end
|
||||
if ( game.SinglePlayer() ) then self:CallOnClient( "SecondaryAttack" ) end
|
||||
local tool = self:GetTool()
|
||||
if not tool or not tool.RightClick then return end
|
||||
local Owner = self:GetOwner()
|
||||
if tool.RightClick(self, Owner:GetEyeTrace()) then
|
||||
self:DoShootEffect(Owner:GetEyeTrace(),IsFirstTimePredicted())
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Holster()
|
||||
if not IsFirstTimePredicted() then return end
|
||||
if ( game.SinglePlayer() ) then self:CallOnClient( "Holster" ) end
|
||||
return true
|
||||
end
|
||||
|
||||
function SWEP:Reload()
|
||||
if not IsFirstTimePredicted() then return end
|
||||
local Owner = self:GetOwner()
|
||||
if ( !Owner:KeyPressed( IN_RELOAD ) ) then return end
|
||||
self:SwitchTool()
|
||||
Owner:EmitSound("buttons/button14.wav")
|
||||
end
|
||||
|
||||
function SWEP:Think()
|
||||
end
|
||||
|
||||
-- Stops players from picking up multiple tools
|
||||
hook.Add("PlayerCanPickupWeapon", "StormFox2.Tool.Pickup", function(ply, wep)
|
||||
if (wep:GetClass() ~= "sf2_tool") then return end -- Ignore other weapons
|
||||
if IsValid(ply:GetWeapon("sf2_tool")) then return false end -- If you already have a tool, don't pick this one up
|
||||
end)
|
||||
333
lua/weapons/sf2_tool/settings/light_editor.lua
Normal file
333
lua/weapons/sf2_tool/settings/light_editor.lua
Normal file
@@ -0,0 +1,333 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
local TOOL = {}
|
||||
TOOL.RealName = "Light Editor"
|
||||
TOOL.PrintName = "#sf_tool.light_editor"
|
||||
TOOL.ToolTip = "#sf_tool.light_editor.desc"
|
||||
TOOL.NoPrintName = false
|
||||
-- TOOL.ShootSound = Sound("weapons/irifle/irifle_fire2.wav")
|
||||
|
||||
local SPAWN = 0
|
||||
local DELETE = 1
|
||||
local SPAWN_ALL = 2
|
||||
local DELETE_ALL= 3
|
||||
|
||||
local INVALID = 0
|
||||
local POINTLIGHT= 1
|
||||
local SPOTLIGHT = 2
|
||||
local FAKESPOT = 3
|
||||
|
||||
local t_models = {}
|
||||
t_models['models/props_c17/lamppost03a_off.mdl'] = {Vector(0,94,440), Angle(0,0,0), SPOTLIGHT}
|
||||
t_models['models/sickness/evolight_01.mdl'] = {Vector(0,-80,314), Angle(0,0,0), SPOTLIGHT}
|
||||
t_models['models/props_lighting/lightfixture02.mdl'] = {Vector(50,0,-10), Angle(30,0,0), FAKESPOT}
|
||||
t_models['models/sickness/parkinglotlight.mdl'] = {Vector(0,30,284), Angle(0,0,0), FAKESPOT, Vector(0,-30,284)}
|
||||
t_models['models/props/de_inferno/light_streetlight.mdl'] = {Vector(0,0,150), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props/cs_office/light_inset.mdl'] = {Vector(0,0,-3), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/unioncity2/props_street/streetlight.mdl'] = {Vector(0,-108,388), Angle(0,0,0), SPOTLIGHT}
|
||||
t_models['models/unioncity2/props_lighting/lightpost_double.mdl']={Vector(5,0,358),Angle(0,0,0), SPOTLIGHT, Vector(-75,0,358)}
|
||||
t_models['models/unioncity2/props_street/telepole01b.mdl'] = {Vector(0,-109,335), Angle(0,0,0), SPOTLIGHT}
|
||||
t_models['models/unioncity2/props_lighting/lightpost_single.mdl']={Vector(76,0,357),Angle(0,0,0), SPOTLIGHT}
|
||||
|
||||
t_models['models/props_badlands/siloroom_light2.mdl'] = {Vector(0,0,-18), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props_badlands/siloroom_light2_small.mdl'] = {Vector(0,0,-14), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props_c17/light_cagelight01_off.mdl'] = {Vector(4,0,-8), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props_c17/light_cagelight02_off.mdl'] = {Vector(4,0,-8), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props_c17/light_cagelight02_on.mdl'] = {Vector(4,0,-8), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props_c17/light_decklight01_off.mdl'] = {Vector(0,0,0), Angle(90,180,0),SPOTLIGHT}
|
||||
t_models['models/props_c17/light_decklight01_on.mdl'] = {Vector(0,0,0), Angle(90,180,0),SPOTLIGHT}
|
||||
t_models['models/props_c17/light_domelight01_off.mdl'] = {Vector(0,0,-8), Angle(0,0,0), POINTLIGHT}
|
||||
t_models['models/props_c17/light_floodlight02_off.mdl'] = {Vector(0,-15,78), Angle(0,275,68),FAKESPOT,Vector(0,15,78), Angle(0,265,68)}
|
||||
t_models['models/props_c17/light_industrialbell01_on.mdl'] = {Vector(0,0,-8), Angle(0,0,0), FAKESPOT}
|
||||
t_models['models/props_combine/combine_light001a.mdl'] = {Vector(-6,0,34), Angle(90,0,0), SPOTLIGHT}
|
||||
t_models['models/props_combine/combine_light001b.mdl'] = {Vector(-12,0,47), Angle(90,0,0), SPOTLIGHT}
|
||||
t_models['models/props_combine/combine_light002a.mdl'] = {Vector(-9,0,37), Angle(90,0,0), SPOTLIGHT}
|
||||
t_models['models/props_equipment/light_floodlight.mdl'] = {Vector(0,-12,80), Angle(0,275,68),FAKESPOT,Vector(0,12,80), Angle(0,265,68)}
|
||||
t_models['models/props_gameplay/security_fence_light01.mdl']= {Vector(0,-68,-11), Angle(0,0,0), SPOTLIGHT}
|
||||
t_models['models/props_wasteland/lights_industrialcluster01a.mdl']= {Vector(-20,0,374),Angle(52,0,0),SPOTLIGHT, Vector(20,0,374), Angle(-52,0,0)}
|
||||
t_models['models/props_mvm/construction_light02.mdl'] = {Vector(-30,-25,144), Angle(0,275,68),FAKESPOT,Vector(-30,25,144), Angle(0,265,68)}
|
||||
t_models['models/props_hydro/construction_light.mdl'] = {Vector(0,-3,-19), Angle(0,0,45), SPOTLIGHT}
|
||||
t_models['models/props/cs_assault/streetlight.mdl'] = {Vector(50,0,45), Angle(0,0,0), SPOTLIGHT}
|
||||
t_models['models/props/cs_italy/it_streetlampleg.mdl']={Vector(0,0,156),Angle(0,0,0), POINTLIGHT}
|
||||
|
||||
local function IsLightNear(pos)
|
||||
local t = {}
|
||||
for k,v in ipairs(ents.FindInSphere(pos, 20)) do
|
||||
if v:GetClass() == "stormfox_streetlight_invisible" then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function SpawnMissingLight(pos, ang, i_type)
|
||||
if IsLightNear(pos) then return end
|
||||
local ent = ents.Create("stormfox_streetlight_invisible")
|
||||
ent:SetPos(pos)
|
||||
ent:SetAngles(ang)
|
||||
ent:Spawn()
|
||||
ent:SetLightType(i_type)
|
||||
return ent
|
||||
end
|
||||
|
||||
local function StaticLocal(v, pos, ang)
|
||||
return LocalToWorld(pos * (v.UniformScale or v.Scale or 1), ang, v.Origin, v.Angles)
|
||||
end
|
||||
|
||||
local function StaticLightPos(v)
|
||||
local tab = t_models[v.PropType]
|
||||
if not tab then return end -- Unknown
|
||||
local pos, ang = StaticLocal(v, tab[1] * (v.UniformScale or v.Scale), tab[2])
|
||||
local spos, ang2
|
||||
if tab[4] then
|
||||
spos, ang2 = StaticLocal(v, tab[4] * (v.UniformScale or v.Scale), tab[5] or tab[2])
|
||||
end
|
||||
return pos, ang, spos, ang2
|
||||
end
|
||||
|
||||
local sorter = function(a,b)
|
||||
return a[5]<b[5]
|
||||
end
|
||||
|
||||
local function FindStaticProps(pos, dis)
|
||||
local ls = StormFox2.Map.FindStaticsInSphere(pos, dis)
|
||||
local t = {}
|
||||
for k, v in ipairs(ls) do
|
||||
if t_models[v.PropType] then
|
||||
table.insert(t, {v.PropType, v.Origin, v.Angles, v.UniformScale or v.Scale or 1, v.Origin:DistToSqr(pos)})
|
||||
else
|
||||
--print(v.PropType)
|
||||
end
|
||||
end
|
||||
if #t < 1 then return end
|
||||
if #t < 2 then return t[1][1],t[1][2],t[1][3],t[1][4] end
|
||||
table.sort(t, sorter)
|
||||
return t[1][1],t[1][2],t[1][3],t[1][4]
|
||||
end
|
||||
|
||||
-- Returns a mdl, pos, ang and scale if found
|
||||
local function FindTraceTarget(tr)
|
||||
local ent = tr.Entity
|
||||
if not ent then return end
|
||||
if ent:IsWorld() then -- Static-Prop?
|
||||
if tr.HitTexture ~= "**studio**" then return end -- Not a static prop
|
||||
return FindStaticProps(tr.HitPos, 200)
|
||||
elseif IsValid(ent) and t_models[ent:GetModel()] then-- Prop
|
||||
return ent:GetModel(), ent:GetPos(), ent:GetAngles(), ent:GetModelScale()
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
-- Spawns all missing lights for said model
|
||||
local function SpawnMissingLights(mdl)
|
||||
if not t_models[mdl] then return end
|
||||
local all_static = StormFox2.Map.StaticProps()
|
||||
for k, v in pairs(all_static) do
|
||||
if v.PropType ~= mdl then continue end
|
||||
local pos, ang, pos2, ang2 = StaticLightPos(v)
|
||||
SpawnMissingLight(pos, ang, t_models[mdl][3])
|
||||
if pos2 then
|
||||
SpawnMissingLight(pos2, ang2, t_models[mdl][3])
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Deletes all light for said model
|
||||
local function DeleteAllLights(mdl)
|
||||
if not t_models[mdl] then return end
|
||||
local all_static = StormFox2.Map.StaticProps()
|
||||
for k, v in pairs(all_static) do
|
||||
if v.PropType ~= mdl then continue end
|
||||
local pos, ang, pos2 = StaticLightPos(v)
|
||||
SafeRemoveEntity(IsLightNear(pos))
|
||||
if pos2 then
|
||||
SafeRemoveEntity(IsLightNear(pos2))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local popsnd = Sound("garrysmod/balloon_pop_cute.wav")
|
||||
function TOOL:SendFunc( a, b, c, d )
|
||||
if a == SPAWN and type(b) == "number" and c and d then -- Spawn, n_type, pos, ang
|
||||
if IsLightNear(c) then return end
|
||||
local ent = ents.Create("stormfox_streetlight_invisible")
|
||||
ent:SetPos(c)
|
||||
ent:SetAngles(d)
|
||||
ent:Spawn()
|
||||
ent:SetLightType(b)
|
||||
self:EmitSound("weapons/ar2/ar2_reload_rotate.wav")
|
||||
elseif a == DELETE and IsValid(b) and b.GetClass then -- Delete, entity
|
||||
if b:GetClass()~="stormfox_streetlight_invisible" then return end -- Can't delete things
|
||||
b:EmitSound(popsnd)
|
||||
SafeRemoveEntity(b)
|
||||
elseif a == SPAWN_ALL then
|
||||
SpawnMissingLights(b)
|
||||
self:EmitSound("weapons/ar2/ar2_reload_rotate.wav")
|
||||
elseif a == DELETE_ALL then
|
||||
DeleteAllLights(b)
|
||||
self:EmitSound("weapons/ar2/ar2_reload_rotate.wav")
|
||||
end
|
||||
end
|
||||
else
|
||||
function TOOL:SpawnLight(n_type, pos, ang, double, ang2)
|
||||
self.SendFunc( SPAWN, n_type, pos, ang )
|
||||
if double then self.SendFunc( SPAWN, n_type, double, ang2 or ang ) end
|
||||
end
|
||||
function TOOL:DeleteLight( ent )
|
||||
if not IsValid(ent) then return end
|
||||
self.SendFunc( DELETE, ent )
|
||||
end
|
||||
function TOOL:SpawnAllLights(mdl)
|
||||
if not t_models[mdl] then return end
|
||||
self.SendFunc( SPAWN_ALL, mdl )
|
||||
end
|
||||
function TOOL:DeleteAllLights(mdl)
|
||||
if not t_models[mdl] then return end
|
||||
self.SendFunc( DELETE_ALL, mdl )
|
||||
end
|
||||
|
||||
local selectedData = {
|
||||
}
|
||||
local ghost
|
||||
-- Render is called right after screenrender
|
||||
local v = Vector(440,40,40)
|
||||
local m_lamp = Material('stormfox2/effects/light_beam')
|
||||
local m_spot = Material('stormfox2/effects/spotlight')
|
||||
function TOOL:Render()
|
||||
if not selectedData.Pos then return end
|
||||
if selectedData.Type == SPOTLIGHT or selectedData.Type == FAKESPOT then
|
||||
render.SetMaterial(m_lamp)
|
||||
render.DrawBeam(selectedData.Pos, selectedData.Pos - selectedData.Ang:Up() * 300, 300, 0, 1, color_white)
|
||||
if selectedData.Pos2 then
|
||||
render.DrawBeam(selectedData.Pos2, selectedData.Pos2 - selectedData.Ang2:Up() * 300, 300, 0, 1, color_white)
|
||||
end
|
||||
elseif selectedData.Type == POINTLIGHT then
|
||||
render.SetMaterial(m_spot)
|
||||
render.DrawSprite(selectedData.Pos, 80, 80, color_white)
|
||||
if selectedData.Pos2 then
|
||||
render.DrawSprite(selectedData.Pos2, 80, 80, color_white)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ghost
|
||||
local col = Color(0,255,0)
|
||||
local ssi_mdl = Model("models/hunter/blocks/cube025x025x025.mdl")
|
||||
local c_outline = "model_color"
|
||||
local a_outline = "models/effects/comball_tape"
|
||||
local default_lighttype = FAKESPOT
|
||||
local function ScanTrace(self,tr)
|
||||
selectedData = {}
|
||||
-- Delete lights if looking at them
|
||||
if IsValid(tr.Entity) and tr.Entity:GetClass() == "stormfox_streetlight_invisible" then
|
||||
ghost = self._swep:SetGhost(tr.Entity:GetModel(), tr.Entity:GetPos(), tr.Entity:GetAngles())
|
||||
self:SetGhostHalo(col)
|
||||
selectedData.deleteEnt = tr.Entity
|
||||
else -- Check for staticprops / props
|
||||
local mdl, pos, ang, scale = FindTraceTarget(tr)
|
||||
if mdl then -- If valid target
|
||||
selectedData.Model = mdl
|
||||
local tab = t_models[mdl]
|
||||
ghost = self._swep:SetGhost(mdl, pos, ang)
|
||||
ghost:SetMaterial(a_outline)
|
||||
if ghost then -- If ghost
|
||||
scale = scale or 1
|
||||
-- Check if ent is there
|
||||
ghost:SetModelScale(scale)
|
||||
ghost:SetColor(Color(255,255,255,255))
|
||||
selectedData.Pos = ghost:LocalToWorld(tab[1] * scale)
|
||||
selectedData.Ang = ghost:LocalToWorldAngles(tab[2])
|
||||
selectedData.Pos2 = tab[4] and ghost:LocalToWorld(tab[4] * scale)
|
||||
selectedData.Ang2 = tab[5] and ghost:LocalToWorldAngles(tab[5]) or selectedData.Ang
|
||||
selectedData.Type = tab[3]
|
||||
local b = IsLightNear(selectedData.Pos)
|
||||
local c = selectedData.Pos2 and IsLightNear(selectedData.Pos2)
|
||||
if b then
|
||||
self:SetGhostHalo(col)
|
||||
selectedData.deleteEnt = b
|
||||
selectedData.deleteEnt2 = c
|
||||
else
|
||||
self:SetGhostHalo(color_white)
|
||||
end
|
||||
end
|
||||
else -- Invalid target
|
||||
selectedData.Type = default_lighttype
|
||||
local ang = tr.HitNormal:Angle()
|
||||
ang:RotateAroundAxis(ang:Right(), 90)
|
||||
ghost = self._swep:SetGhost(ssi_mdl, tr.HitPos, ang)
|
||||
if ghost then
|
||||
ghost:SetMaterial(c_outline)
|
||||
selectedData.Pos = ghost:GetPos()
|
||||
selectedData.Ang = (input.IsKeyDown( KEY_LCONTROL ) or input.IsKeyDown( KEY_RCONTROL )) and Angle(0,0,0) or ghost:GetAngles()
|
||||
if default_lighttype == POINTLIGHT then
|
||||
selectedData.Pos = selectedData.Pos + tr.HitNormal
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local m1 = Material("effects/lamp_beam")
|
||||
local m2 = Material("sprites/glow04_noz")
|
||||
local m3 = Material("effects/flashlight001")
|
||||
function TOOL:ScreenRender( w, h )
|
||||
ScanTrace(self,LocalPlayer():GetEyeTrace())
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.DrawOutlinedRect(w * 0.1,h * 0.2,w * 0.8,h * 0.7)
|
||||
local l_type = selectedData.Type or default_lighttype
|
||||
surface.SetDrawColor(255,255,255)
|
||||
if l_type == POINTLIGHT then
|
||||
surface.SetMaterial(m2)
|
||||
elseif l_type == FAKESPOT then
|
||||
surface.SetMaterial(m1)
|
||||
elseif l_type == SPOTLIGHT then
|
||||
surface.SetMaterial(m3)
|
||||
surface.DrawTexturedRect(w * 0.1, h * 0.4, w * 0.8, h * 0.7)
|
||||
surface.SetMaterial(m1)
|
||||
end
|
||||
|
||||
surface.DrawTexturedRect(w * 0.1, h * 0.2, w * 0.8, h * 0.7)
|
||||
end
|
||||
end
|
||||
function TOOL:LeftClick(tr)
|
||||
if not IsValid(ghost) then return end
|
||||
local select_all = input.IsKeyDown( KEY_LCONTROL ) or input.IsKeyDown( KEY_RCONTROL )
|
||||
-- If we hold control in. Spawn all lights for given model
|
||||
if select_all and selectedData.Model then
|
||||
if selectedData.Model then
|
||||
self:SpawnAllLights(selectedData.Model)
|
||||
return
|
||||
end
|
||||
elseif not selectedData.deleteEnt and selectedData.Pos then -- Only spawn light, if we aren't looking at an entity atm
|
||||
self:SpawnLight(selectedData.Type, selectedData.Pos, selectedData.Ang, selectedData.Pos2, selectedData.Ang2)
|
||||
-- selectedData.Pos = nil
|
||||
end
|
||||
end
|
||||
function TOOL:RightClick(tr)
|
||||
if self:IsContextMenuOpen() then return end -- Don't delete the entity if you rightclick it for properties.
|
||||
-- Delete all lights for the given model (If not valid, then do nothing)
|
||||
local select_all = input.IsKeyDown( KEY_LCONTROL ) or input.IsKeyDown( KEY_RCONTROL )
|
||||
if select_all then
|
||||
if selectedData.Model then
|
||||
self:DeleteAllLights(selectedData.Model)
|
||||
end
|
||||
return
|
||||
end
|
||||
-- Delete the entity we look at
|
||||
if selectedData.deleteEnt then
|
||||
self:DeleteLight(selectedData.deleteEnt)
|
||||
self:DeleteLight(selectedData.deleteEnt2)
|
||||
else -- If no entity, then swap the lighttype.
|
||||
default_lighttype = default_lighttype + 1
|
||||
if default_lighttype > 3 then default_lighttype = 1 end
|
||||
end
|
||||
end
|
||||
end
|
||||
return TOOL
|
||||
148
lua/weapons/sf2_tool/settings/surface_editor.lua
Normal file
148
lua/weapons/sf2_tool/settings/surface_editor.lua
Normal file
@@ -0,0 +1,148 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local TOOL = {}
|
||||
TOOL.RealName = "Surface Editor"
|
||||
TOOL.PrintName = "#sf_tool.surface_editor"
|
||||
TOOL.ToolTip = "#sf_tool.surface_editor.desc"
|
||||
TOOL.NoPrintName = false
|
||||
TOOL.ShootSound = Sound("weapons/irifle/irifle_fire2.wav")
|
||||
|
||||
local mat = Material("stormfox2/weapons/sf_tool_mat")
|
||||
local function FindTexture( str )
|
||||
str = str:lower()
|
||||
if str == "**displacement**" then return end
|
||||
if str == "**studio**" then return end
|
||||
if str:sub(0,5) == "tools" then return end
|
||||
local mat = Material(str)
|
||||
--if str:sub(0,5) == "maps/" and false then -- This is a hammer thingy
|
||||
-- str = mat:GetString( "$basetexture" ) or mat:GetString( "$basetexture2" )
|
||||
-- mat = Material(str)
|
||||
-- if StormFox2.Terrain.HasMaterialChanged(mat) then -- We havve replaced this material. Lets get the basetexture
|
||||
-- return StormFox2.Terrain.GetOriginalTexture(mat)
|
||||
-- end
|
||||
--end
|
||||
return str
|
||||
end
|
||||
|
||||
local cross = Material("gui/cross.png")
|
||||
local c_red = Color(255,55,55)
|
||||
|
||||
local m_roof = Material("stormfox2/hud/tool/texture_roof.png")
|
||||
local m_ground = Material("stormfox2/hud/tool/texture_ground.png")
|
||||
|
||||
local snd_accept = Sound("buttons/button3.wav")
|
||||
local snd_deny = Sound("buttons/button2.wav")
|
||||
|
||||
if SERVER then
|
||||
function TOOL:SendFunc( tex, a )
|
||||
if not tex or not a then return end
|
||||
if type(tex) ~= "string" or type(a) ~= "number" then return end
|
||||
StormFox2.Map.ModifyMaterialType( tex, a )
|
||||
self:EmitSound(snd_accept)
|
||||
end
|
||||
else
|
||||
local function OpenOption( self, sTexture )
|
||||
local p = vgui.Create("DFrame")
|
||||
p:SetTitle(language.GetPhrase("spawnmenu.menu.edit"))
|
||||
p:SetSize( 50 * 3 + 10, 50 + 24)
|
||||
p:Center()
|
||||
p:MakePopup()
|
||||
-- Roof
|
||||
local roof = vgui.Create( "DImageButton", p )
|
||||
roof:SetSize( 50, 50)
|
||||
roof:SetImage("stormfox2/hud/tool/texture_roof.png")
|
||||
roof:Dock(LEFT)
|
||||
roof.DoClick = function()
|
||||
self.SendFunc( sTexture, 1 )
|
||||
p:Remove()
|
||||
end
|
||||
-- Roof
|
||||
local ground = vgui.Create( "DImageButton", p )
|
||||
ground:SetSize( 50, 50)
|
||||
ground:SetImage("stormfox2/hud/tool/texture_ground.png")
|
||||
ground:Dock(LEFT)
|
||||
ground.DoClick = function()
|
||||
self.SendFunc( sTexture, 0 )
|
||||
p:Remove()
|
||||
end
|
||||
-- Block
|
||||
local block = vgui.Create( "DImageButton", p )
|
||||
block:SetSize( 50, 50)
|
||||
block:SetImage("gui/cross.png")
|
||||
block:Dock(LEFT)
|
||||
block.DoClick = function()
|
||||
self.SendFunc( sTexture, -1 )
|
||||
p:Remove()
|
||||
end
|
||||
end
|
||||
function TOOL:LeftClick(tr)
|
||||
local tex = FindTexture(tr.HitTexture)
|
||||
if not tex then
|
||||
self:EmitSound(snd_deny)
|
||||
return
|
||||
end
|
||||
OpenOption( self, tex )
|
||||
end
|
||||
function TOOL:RightClick(tr)
|
||||
local tex = FindTexture(tr.HitTexture)
|
||||
if not tex then
|
||||
self:EmitSound(snd_deny)
|
||||
return
|
||||
end
|
||||
self.SendFunc( tex, -2, -2 )
|
||||
self:EmitSound(snd_accept)
|
||||
end
|
||||
end
|
||||
|
||||
function TOOL:ScreenRender( w, h )
|
||||
local tr = LocalPlayer():GetEyeTrace()
|
||||
local tex = FindTexture(tr.HitTexture)
|
||||
if tex then
|
||||
mat:SetTexture("$basetexture", tex)
|
||||
-- In case this material doesn't have a valid texture, it might be only a material.
|
||||
if not mat:GetTexture("$basetexture") then
|
||||
local m = Material(tex)
|
||||
local tryTex = StormFox2.Terrain.GetOriginalTexture(m) or m:GetTexture("$basetexture")
|
||||
if tryTex then
|
||||
mat:SetTexture("$basetexture", tryTex)
|
||||
end
|
||||
end
|
||||
local tData = SF_TEXTDATA[tex]
|
||||
surface.SetMaterial(mat)
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.DrawTexturedRect(w * 0.1,h * 0.2,w * 0.8,h * 0.7)
|
||||
if tData and tData[1] then
|
||||
-- Roof type
|
||||
if tData[1] == -1 then
|
||||
surface.SetDrawColor(c_red)
|
||||
surface.SetMaterial(cross)
|
||||
surface.DrawTexturedRect(w * 0.15,h * 0.65,w * 0.2,h * 0.2)
|
||||
elseif tData[1] == 0 then
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(m_ground)
|
||||
surface.DrawTexturedRect(w * 0.15,h * 0.65,w * 0.2,h * 0.2)
|
||||
elseif tData[1] == 1 then
|
||||
surface.SetDrawColor(color_white)
|
||||
surface.SetMaterial(m_roof)
|
||||
surface.DrawTexturedRect(w * 0.15,h * 0.65,w * 0.2,h * 0.2)
|
||||
end
|
||||
end
|
||||
else
|
||||
surface.SetDrawColor(c_red)
|
||||
surface.SetMaterial(cross)
|
||||
surface.DrawTexturedRect(w * 0.1,h * 0.2,w * 0.8,h * 0.7)
|
||||
surface.SetDrawColor(color_white)
|
||||
end
|
||||
|
||||
surface.DrawOutlinedRect(w * 0.1,h * 0.2,w * 0.8,h * 0.7)
|
||||
end
|
||||
return TOOL
|
||||
171
lua/weapons/sf2_tool/shared.lua
Normal file
171
lua/weapons/sf2_tool/shared.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
--[[
|
||||
| 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.PrintName = "#sf_tool.name"
|
||||
SWEP.Author = "Nak"
|
||||
SWEP.Contact = ""
|
||||
SWEP.Purpose = "#sf_tool.desc"
|
||||
SWEP.Instructions = "#sf_tool.desc"
|
||||
|
||||
SWEP.ViewModel = "models/weapons/c_toolgun.mdl"
|
||||
SWEP.WorldModel = "models/weapons/w_toolgun.mdl"
|
||||
|
||||
SWEP.UseHands = true
|
||||
SWEP.Spawnable = true
|
||||
SWEP.AdminOnly = true
|
||||
|
||||
SWEP.Slot = 5
|
||||
SWEP.SlotPos = 5
|
||||
|
||||
util.PrecacheModel( SWEP.ViewModel )
|
||||
util.PrecacheModel( SWEP.WorldModel )
|
||||
|
||||
-- Tool meta
|
||||
local t_meta = {}
|
||||
-- Proxy allows to push entity functions within TOOL to SWEP. Its a hack, but I'm lazy.
|
||||
local proxy_key,proxy_self
|
||||
local function proxy(...)
|
||||
local self = proxy_self
|
||||
local func = self[proxy_key]
|
||||
local a = {...}
|
||||
-- In case first argument is "self", weplace it with SWEP
|
||||
if #a > 0 then
|
||||
if type(a[1]) == "table" and a[1].MetaName and a[1].MetaName == "sftool" then
|
||||
a[1] = self
|
||||
end
|
||||
end
|
||||
func(unpack(a))
|
||||
proxy_key = nil
|
||||
proxy_self = nil
|
||||
end
|
||||
t_meta.__index = function(self, key)
|
||||
if key == "_swep" then return end
|
||||
if IsValid(self._swep) and self._swep[key] then
|
||||
proxy_key = key
|
||||
proxy_self = self._swep
|
||||
return proxy
|
||||
end
|
||||
end
|
||||
function t_meta:GetSWEP()
|
||||
return self._swep
|
||||
end
|
||||
|
||||
-- Load tools
|
||||
SWEP.Tool = {}
|
||||
|
||||
for _,fil in ipairs(file.Find("weapons/sf2_tool/settings/*.lua","LUA")) do
|
||||
if SERVER then
|
||||
AddCSLuaFile("weapons/sf2_tool/settings/" .. fil)
|
||||
end
|
||||
local tool = (include("weapons/sf2_tool/settings/" .. fil))
|
||||
tool.MetaName = "sftool"
|
||||
setmetatable(tool, t_meta)
|
||||
table.insert(SWEP.Tool, tool)
|
||||
end
|
||||
|
||||
SWEP.Primary.ClipSize = -1
|
||||
SWEP.Primary.DefaultClip = -1
|
||||
SWEP.Primary.Automatic = false
|
||||
SWEP.Primary.Ammo = "none"
|
||||
|
||||
SWEP.Secondary.ClipSize = -1
|
||||
SWEP.Secondary.DefaultClip = -1
|
||||
SWEP.Secondary.Automatic = false
|
||||
SWEP.Secondary.Ammo = "none"
|
||||
|
||||
SWEP.CanHolster = true
|
||||
SWEP.CanDeploy = true
|
||||
|
||||
function SWEP:SetupDataTables()
|
||||
self:NetworkVar( "Int", 0, "ToolID" )
|
||||
end
|
||||
|
||||
function SWEP:SetTool(num)
|
||||
self._toolobj = nil
|
||||
if not IsValid(self:GetOwner()) then return end
|
||||
if SERVER then
|
||||
self:SetToolID( num )
|
||||
end
|
||||
if num == 0 then return end -- Screen
|
||||
self._toolobj = table.Copy(self.Tool[num])
|
||||
self._toolobj._swep = self
|
||||
setmetatable(self._toolobj, t_meta)
|
||||
return self._toolobj
|
||||
end
|
||||
|
||||
function SWEP:GetTool()
|
||||
if not IsValid(self:GetOwner()) then return end -- No owner.
|
||||
if self._toolobj then
|
||||
return self._toolobj
|
||||
end
|
||||
local n = self:GetToolID()
|
||||
if n == 0 then return end
|
||||
self:SetTool(self:GetToolID())
|
||||
return self._toolobj
|
||||
end
|
||||
|
||||
function SWEP:DoShootEffect( tr, bFirstTimePredicted )
|
||||
self:SendWeaponAnim( ACT_VM_PRIMARYATTACK ) -- View model animation
|
||||
local Owner = self:GetOwner()
|
||||
Owner:SetAnimation( PLAYER_ATTACK1 )
|
||||
if ( not bFirstTimePredicted ) then return end
|
||||
local traceEffect = EffectData()
|
||||
traceEffect:SetOrigin( tr.HitPos + tr.HitNormal * 4 )
|
||||
traceEffect:SetStart( Owner:GetShootPos() )
|
||||
traceEffect:SetAttachment( 1 )
|
||||
traceEffect:SetEntity( self )
|
||||
traceEffect:SetScale(0.2)
|
||||
traceEffect:SetNormal( tr.HitNormal )
|
||||
util.Effect( "ToolTracer", traceEffect )
|
||||
util.Effect( "StunstickImpact", traceEffect )
|
||||
local tool = self:GetTool()
|
||||
if not tool or not tool.ShootSound then return end
|
||||
Owner:EmitSound(tool.ShootSound)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local function dofunction(ply, wep, tool, data)
|
||||
StormFox2.Msg(ply:GetName(),color_white," used",tool.RealName or "SF2 Tool.")
|
||||
tool.SendFunc( wep, unpack( data ) )
|
||||
wep:DoShootEffect(ply:GetEyeTrace(),IsFirstTimePredicted())
|
||||
end
|
||||
net.Receive(StormFox2.Net.Tool, function(len, ply)
|
||||
local wep = ply:GetActiveWeapon()
|
||||
if not IsValid(wep) then return end
|
||||
if wep:GetClass() ~= "sf2_tool" then return end
|
||||
local tool = wep:GetTool()
|
||||
if not tool or not tool.SendFunc then return end
|
||||
wep:HasAccessToSettings(dofunction, ply, wep, tool, net.ReadTable() )
|
||||
end)
|
||||
else
|
||||
function SWEP.SendFunc( ... )
|
||||
net.Start(StormFox2.Net.Tool)
|
||||
net.WriteTable({...})
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
|
||||
function SWEP:Initialize()
|
||||
self:SetHoldType( "revolver" )
|
||||
self.Primary = {
|
||||
ClipSize = -1,
|
||||
DefaultClip = -1,
|
||||
Automatic = false,
|
||||
Ammo = "none"
|
||||
}
|
||||
self.Secondary = {
|
||||
ClipSize = -1,
|
||||
DefaultClip = -1,
|
||||
Automatic = false,
|
||||
Ammo = "none"
|
||||
}
|
||||
end
|
||||
Reference in New Issue
Block a user