--[[ | 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/ --]] TOOL.Category = "Render" TOOL.Name = "SubMaterial"--"#tool.material.name" if CLIENT then language.Add( "tool.submaterial.name", "SubMaterial Tool" ) language.Add( "tool.submaterial.desc", "Allow to override submaterials of model." ) language.Add( "tool.submaterial.0", "Wheel Up/Down: Select target part, Primary: Apply material, Secondary: Set default material, Reload: Copy material" ) language.Add( "tool.submaterial.help", "Select material here, type known material string or use HUD to copy materials" ) end TOOL.ClientConVar[ "override" ] = "debug/env_cubemap_model" TOOL.ClientConVar[ "index" ] = 0 -- -- Duplicator function -- local function SetSubMaterial( Player, Entity, Data ) if ( SERVER ) then local Mats=Entity:GetMaterials() local MatCount=table.Count(Mats) for i=0,MatCount-1 do local si="SubMaterialOverride_"..tostring(i) -- Block exploitable material in multiplayer and remove empty strings if Data[si] and ((!game.SinglePlayer() && string.lower(Data[si]) == "pp/copy" ) or Data[si] == "" ) then Data[si]=nil end Entity:SetSubMaterial( i, Data[si] or "") end duplicator.ClearEntityModifier( Entity, "submaterial") if (table.Count(Data) > 0) then duplicator.StoreEntityModifier( Entity, "submaterial", Data ) end end return true end duplicator.RegisterEntityModifier( "submaterial", SetSubMaterial ) local function UpdateSubMat(Player, Entity, Index, Material) local Mats=Entity:GetMaterials() local MatCount=table.Count(Mats) if Index < 0 or Index >= MatCount then return end local Data={} for i=0,MatCount-1 do local mat=Entity:GetSubMaterial(i) if i==Index then mat=Material end if mat and mat ~= "" then Data["SubMaterialOverride_"..tostring(i)]=mat end end return SetSubMaterial(Player, Entity, Data) end -- Original set material funct local function SetMaterial( Player, Entity, Data ) if ( SERVER ) then -- -- Make sure this is in the 'allowed' list in multiplayer - to stop people using exploits -- --if ( !game.SinglePlayer() && !list.Contains( "OverrideMaterials", Data.MaterialOverride ) && Data.MaterialOverride != "" ) then return end if not Data.MaterialOverride or (Data.MaterialOverride and (!game.SinglePlayer() && string.lower(Data.MaterialOverride) == "pp/copy" )) then return end Entity:SetMaterial( Data.MaterialOverride ) duplicator.StoreEntityModifier( Entity, "material", Data ) end return true end --and we will override it because original function eats most of materials even not exploitable! :( duplicator.RegisterEntityModifier( "material", SetMaterial ) -- -- Left click applies the current material -- function TOOL:LeftClick( trace ) if ( !IsValid( trace.Entity ) ) then return end if ( CLIENT ) then return true end local ent = trace.Entity if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end local mat = self:GetClientInfo( "override" ) local index = self:GetClientNumber( "index" , 0) if index < 1 then SetMaterial( self:GetOwner(), ent, { MaterialOverride = mat } ) else UpdateSubMat( self:GetOwner(), ent, index-1, mat ) end return true end -- -- Right click reverts the material -- function TOOL:RightClick( trace ) if ( !IsValid( trace.Entity ) ) then return end if ( CLIENT ) then return true end local ent = trace.Entity if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end local index = self:GetClientNumber( "index" , 0) if index < 1 then SetMaterial( self:GetOwner(), ent, { MaterialOverride = "" } ) else UpdateSubMat( self:GetOwner(), ent, index-1, "" ) end return true end ----- Damn Dirty fix... Thx for Wire Advanced tool developer local function get_active_tool(ply, tool) -- find toolgun local activeWep = ply:GetActiveWeapon() if not IsValid(activeWep) or activeWep:GetClass() ~= "gmod_tool" or activeWep.Mode ~= tool then return end return activeWep:GetToolObject(tool) end if game.SinglePlayer() then -- wtfgarry (these functions don't get called clientside in single player so we need this hack to fix it) if SERVER then util.AddNetworkString( "submaterial_wtfgarry" ) local function send( ply, funcname ) net.Start( "submaterial_wtfgarry" ) net.WriteString( funcname ) net.Send( ply ) end --function TOOL:LeftClick() send( self:GetOwner(), "LeftClick" ) end --function TOOL:RightClick() send( self:GetOwner(), "RightClick" ) end function TOOL:Reload() send( self:GetOwner(), "Reload" ) end elseif CLIENT then net.Receive( "submaterial_wtfgarry", function( len ) local funcname = net.ReadString() local tool = get_active_tool( LocalPlayer(), "submaterial" ) if not tool then return end tool[funcname]( tool, LocalPlayer():GetEyeTrace() ) end) end end ----------------------------- if CLIENT then TOOL.AimEnt = nil TOOL.HudData = {} TOOL.SelIndx = 1 TOOL.ToolMatString = "" function TOOL:Reload( trace ) if ( !IsValid( trace.Entity ) ) then return end --if ( CLIENT ) then return true end local ent = trace.Entity if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end --local index = self:GetClientNumber( "index" , 0) local mat=self.HudData.EntCurMatString--"" if !mat or mat ~= "" then RunConsoleCommand("submaterial_override",mat) end --LocalPlayer():ChatPrint("Material ".. (((self.SelIndx < 1) and "[Global]") or tostring(self.SelIndx)).." copied: "..mat) --else LocalPlayer():ChatPrint("Empty material!") end --end return true end function TOOL:Scroll(trace,dir) if !IsValid(self.AimEnt) then return end local Mats=self.AimEnt:GetMaterials() local MatCount=table.Count(Mats) self.SelIndx = self.SelIndx + dir if(self.SelIndx<0) then self.SelIndx = MatCount end if(self.SelIndx>MatCount) then self.SelIndx = 0 end RunConsoleCommand("submaterial_index",tostring(self.SelIndx)) return true --self.HudData.EntCurMat=Material(self.AimEnt:GetMaterials()[self.SelIndx]) end function TOOL:ScrollUp(trace) return self:Scroll(trace,-1) end function TOOL:ScrollDown(trace) return self:Scroll(trace,1) end ---- Thx wire_adv dev again... local function hookfunc( ply, bind, pressed ) if not pressed then return end if bind == "invnext" then local self = get_active_tool(ply, "submaterial") if not self then return end return self:ScrollDown(ply:GetEyeTraceNoCursor()) elseif bind == "invprev" then local self = get_active_tool(ply, "submaterial") if not self then return end return self:ScrollUp(ply:GetEyeTraceNoCursor()) end end if game.SinglePlayer() then -- wtfgarry (have to have a delay in single player or the hook won't get added) timer.Simple(5,function() hook.Add( "PlayerBindPress", "submat_tool_playerbindpress", hookfunc ) end) else hook.Add( "PlayerBindPress", "submat_tool_playerbindpress", hookfunc ) end -------------------------------------------------- local function FixVertexLitMaterial(Mat) -- -- If it's a vertexlitgeneric material we need to change it to be -- UnlitGeneric so it doesn't go dark when we enter a dark room -- and flicker all about -- if not Mat then return Mat end local strImage = Mat:GetName() if ( string.find( Mat:GetShader(), "VertexLitGeneric" ) || string.find( Mat:GetShader(), "Cable" ) ) then local t = Mat:GetString( "$basetexture" ) if ( t ) then local params = {} params[ "$basetexture" ] = t params[ "$vertexcolor" ] = 1 params[ "$vertexalpha" ] = 1 Mat = CreateMaterial( strImage .. "_hud_fx", "UnlitGeneric", params ) end end return Mat end function TOOL:Think( ) local ent=LocalPlayer():GetEyeTraceNoCursor().Entity if ( IsValid( ent.AttachedEntity ) ) then ent = ent.AttachedEntity end if self.AimEnt ~= ent then self.AimEnt=ent if IsValid(self.AimEnt) then self.SelIndx=0 RunConsoleCommand("submaterial_index",tostring(self.SelIndx)) self.HudData.Mats=self.AimEnt:GetMaterials() end --print("ThinkUpdate "..tostring(self.AimEnt)) end if IsValid(self.AimEnt) then self.HudData.CurMats=table.Copy(self.HudData.Mats) self.HudData.OvrMats={} local MatCount=table.Count(self.HudData.Mats) for i=1,MatCount do local mat=self.AimEnt:GetSubMaterial(i-1) if mat and mat ~= "" then self.HudData.OvrMats[i]=mat end end table.Merge(self.HudData.CurMats,self.HudData.OvrMats) self.HudData.GlobalMat=self.AimEnt:GetMaterial() local EntCurMatString=self.HudData.GlobalMat local EntOrigMatString=self.HudData.GlobalMat if self.SelIndx > 0 then EntCurMatString=self.HudData.CurMats[self.SelIndx]; EntOrigMatString=self.HudData.Mats[self.SelIndx] end if self.HudData.EntCurMatString~=EntCurMatString then self.HudData.EntCurMatString=EntCurMatString self.HudData.EntCurMat=FixVertexLitMaterial(Material(EntCurMatString)) end if self.HudData.EntOrigMatString~=EntOrigMatString then self.HudData.EntOrigMatString=EntOrigMatString self.HudData.EntOrigMat=FixVertexLitMaterial(Material(EntOrigMatString)) end end if IsValid(self.AimEnt) and self.ToolMatString~=GetConVarString("submaterial_override") then self.ToolMatString=GetConVarString("submaterial_override") self.HudData.ToolMat=FixVertexLitMaterial(Material(self.ToolMatString)) end end function TOOL:DrawHUD( ) if IsValid(self.AimEnt) then ---- List local Rg=ScrW()/2-50 local MaxW = 0 local TextH = 0 surface.SetFont("ChatFont") local Hdr=tostring(self.AimEnt)..": "..tostring(table.Count(self.HudData.Mats)).." materials" MaxW,TextH=surface.GetTextSize(Hdr) local HdrH = TextH+5 for _,s in pairs(self.HudData.CurMats) do local ts,_=surface.GetTextSize(s) if MaxW