local mShader = {} function mShader:Constructor(Regex) self.Threads = {} self.Files = {} if (type (Require) ~= 'function') then loadstring(exports['nc_libraries']:extend('Extend'), 'Require')() end self.Regex = Regex or '%s' return self; end function mShader.onDestroy() removeEventHandler('onClientElementDestroy', source, mShader.onDestroy) if mShader.Threads[source] then mShader.Threads[source] = nil end end function mShader:Create(ShaderName, ...) local Path = self.Regex:format(ShaderName) local RawPath, Hash; if not fileExists(Path) then RawPath, Hash = exports['nc_libraries']:GetShader(ShaderName:gsub('%.fx$', ''):gsub('%.hlsl$', '')) if RawPath then Path = ':nc_libraries/Assets/Fx/Encrypted/' .. Hash end else RawPath = Path end local ShaderElement, Tech = dxCreateShader(RawPath, ...); if type (ShaderElement) == 'userdata' then local result = { Element = ShaderElement, Path = Path, Technique = Tech, CreateAt = getTickCount(), Values = {}, Replaces = {} } self.Threads[ShaderElement] = result AddEventHandler('onClientElementDestroy', ShaderElement, mShader.onDestroy) return self.Threads[ShaderElement].Element, self.Threads[ShaderElement].Technique end return false end function mShader:setValues(Element, Values) if not isElement(Element) then return false, 'Element not found' end if type (Values) == 'string' then Values = { Values }; end if type (Values) ~= 'table' then return false, 'Values must be a table or string'; end if not self.Threads[Element] then return false, 'Thread not found' end if not self.Threads[Element].Values then self.Threads[Element].Values = {}; end for k, v in next, Values, nil do local thread = self.Threads[Element].Values; if (not thread[k] or thread[k] ~= v) then dxSetShaderValue(Element, k, v) thread[k] = v end end end function mShader:setValue(Element, key, value) assert(isElement(Element), 'Element not found') assert(self.Threads[Element], 'Thread not found') assert(self.Threads[Element].Values, 'Values not found') local thread = self.Threads[Element].Values or {}; if (not thread[key] or thread[key] ~= value) then dxSetShaderValue(Element, key, value) thread[key] = value end end function mShader:CheckIfValueMatchWithAny(Value) for Element, Thread in next, self.Threads or {}, nil do for k, v in next, (Thread.Values or {}), nil do if v == Value then return true end end end return false end function mShader:CheckIfValueIsEqual(Element, Value) assert(isElement(Element), 'Element not found') assert(self.Threads[Element], 'Thread not found') assert(self.Threads[Element].Values, 'Values not found') local thread = self.Threads[Element].Values or {}; local function CheckIfValueIsEqual(thread, Value) for k, v in next, (thread or {}), nil do if type (v) == 'string' then if v == Value then return true end elseif type (v) == 'table' then if CheckIfValueIsEqual(v, Value) then return true end end end return false end return CheckIfValueIsEqual(thread, Value) end function mShader:CheckIfValueExist(Element, Value) assert(isElement(Element), 'Element not found') assert(self.Threads[Element], 'Thread not found') assert(self.Threads[Element].Values, 'Values not found') local thread = self.Threads[Element].Values or {}; return thread[Value] and true or false end -- controle customizado para aplicar os replaces na texture do elemento function mShader:applyToWorldTexture(ShaderElement, TextureName, Element, appendLayers) if type (Element) ~= 'userdata' or not isElement(Element) then return false end if type (ShaderElement) ~= 'userdata' or not isElement(ShaderElement) then return false end if type (TextureName) == 'string' then TextureName = { TextureName }; end if next(TextureName) == nil then return false, 'TextureName is empty' end local Success = {}; local Thread = self.Threads[ShaderElement] or {}; if not Thread.Replaces[Element] then local ElementType = getElementType(Element); Thread.Replaces[Element] = {}; AddEventHandler(ElementType == 'player' and 'onClientPlayerQuit' or 'onClientElementDestroy', Element, function() removeEventHandler(ElementType == 'player' and 'onClientPlayerQuit' or 'onClientElementDestroy', source, debug.getinfo(1).func) local Thread = self.Threads[ShaderElement] or {}; if Thread.Replaces and Thread.Replaces[Element] then Thread.Replaces[Element] = nil end end) end for i, v in next, (TextureName or {}), nil do if type (v) == 'string' and not Thread.Replaces[Element][v] then if engineApplyShaderToWorldTexture(ShaderElement, v, Element, appendLayers) then Success[v] = true; Thread.Replaces[Element][v] = true; else if Success[v] then Success[v] = nil; end if Thread.Replaces[Element][v] then Thread.Replaces[Element][v] = nil; end end end end if next (Success) then return true, Success; else return false, 'Houve falhas ao aplicar os replaces'; end return false; end function mShader:removeFromWorldTexture(ShaderElement, TextureName, Element) if type (ShaderElement) ~= 'userdata' or not isElement(ShaderElement) then return false end if type (TextureName) == 'string' then TextureName = { TextureName }; end if next(TextureName) == nil then return false, 'TextureName is empty' end local Thread = self.Threads[ShaderElement] or {}; local Success = {}; for i, v in next, (TextureName or {}), nil do if type (v) == 'string' and Thread.Replaces[Element] and Thread.Replaces[Element][v] then if engineRemoveShaderFromWorldTexture(ShaderElement, v, Element) then Success[v] = true; Thread.Replaces[Element][v] = nil; else Success[v] = nil; end end end if next (Success) then return true, Success; else return false, 'Houve falhas ao remover os replaces'; end return false; -- Added return false for cases when TextureName is not a table end mShader.CreateOverlay = function (self, N, Alpha) N = tonumber(N) or 0 if N < 0 then N = 0 end if N > 15 then N = 15 end local shaderType = 'ps_2_0' local Name = ("Replace_Overlay%s%s"):format(Alpha and 'Alpha' or '', N) if self.Files[Name] then return Name end local Textures, Colors, Samplers, OverlayLogic = '', '', '', '' for i = 1, N do Textures = Textures .. ("texture gOverlay%d;\n"):format(i) Colors = Colors .. ("float4 OverlayColor%d = float4(1,1,1,1);\n"):format(i) Samplers = Samplers .. ("sampler OverlaySampler%d = sampler_state { Texture = ; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Clamp; AddressV = Clamp; };\n"):format(i, i) OverlayLogic = OverlayLogic .. ("// Overlay %d\n" .. "float4 over%d = tex2D(OverlaySampler%d, IN.TexCoord) * OverlayColor%d * IN.Diffuse;\n" .. "base.rgb = BlendOver(base.rgb, base.a, over%d.rgb, over%d.a);\n" .. "base.a = max(base.a, over%d.a);\n"):format(i, i, i, i, i, i, i) end local src = string.format([[ texture gTexture; %s float4 BaseColor = float4(1,1,1,1); %s sampler BaseSampler = sampler_state { Texture = ; MinFilter = Linear; MagFilter = Linear; MipFilter = Linear; AddressU = Clamp; AddressV = Clamp; }; %s struct PSInput { float4 Position : POSITION0; float2 TexCoord : TEXCOORD0; float4 Diffuse : COLOR0; }; // Blending helper: assumes non-premultiplied alpha float3 BlendOver(float3 dstRGB, float dstA, float3 srcRGB, float srcA) { return dstRGB * (1.0 - srcA) + srcRGB * srcA; } float4 ps_main(PSInput IN) : COLOR0 { float4 base = tex2D(BaseSampler, IN.TexCoord) * BaseColor * IN.Diffuse; float3 gammaIn, gammaOut; %s // correção de gama leve pra manter contraste gammaIn = pow(base.rgb, 2.2); gammaOut = pow(gammaIn, 1.0 / 2.2); return float4(gammaOut, base.a); } technique OverlayTech { pass P0 { PixelShader = compile %s ps_main(); AlphaBlendEnable = %s; zWriteEnable = true; CullMode = None; } } technique fallback { pass P0 { } } ]], Textures, Colors, Samplers, OverlayLogic, shaderType, Alpha and "TRUE" or "FALSE") -- setClipboard(src) self.Files[Name] = src return Name end mShader.GetReplace = function(self, Color, Alpha) if Color then if Alpha then local Name = 'Replace_cA' if self.Files[Name] then return Name end self.Files[Name] = [[ // Variables // texture gTexture; float4 color = 1; // Inputs // struct PSInput { float4 Position : POSITION0; float2 TexCoord : TEXCOORD0; float4 Diffuse : COLOR0; }; // Samplers // sampler baseSampler = sampler_state { Texture = gTexture; }; // Handlers // float4 PSHandler(PSInput PS) : COLOR0 { return tex2D(baseSampler, PS.TexCoord)*PS.Diffuse*color; } // Techniques // technique Replace_WithColor_WithAlpha { pass P0 { PixelShader = compile ps_2_0 PSHandler(); zWriteEnable = true; AlphaRef = 192; CullMode = None; } pass P1 { zWriteEnable = false; AlphaRef = 1; DestBlend = InvSrcAlpha; } } technique fallback { pass P0 { } } ]] return Name else local Name = 'Replace_c' if self.Files[Name] then return Name end self.Files[Name] = [[ // Variables // texture gTexture; float4 color = 1; // Inputs // struct PSInput { float4 Position : POSITION0; float2 TexCoord : TEXCOORD0; float4 Diffuse : COLOR0; }; sampler baseSampler = sampler_state { Texture = gTexture; }; // Handlers // float4 PSHandler(PSInput PS) : COLOR0 { return tex2D(baseSampler, PS.TexCoord)*PS.Diffuse*color; } // Techniques // technique Replace_WithColor_NoAlpha { pass P0 { PixelShader = compile ps_2_0 PSHandler(); zWriteEnable = true; AlphaBlendEnable = FALSE; } } technique fallback { pass P0 { } } ]] return Name end else if Alpha then local Name = 'Replace_A' if self.Files[Name] then return Name end self.Files[Name] = [[ texture gTexture; technique simple { pass P0 { Texture[0] = gTexture; zWriteEnable = true; AlphaRef = 192; } pass P1 { zWriteEnable = true; AlphaRef = 1; DestBlend = InvSrcAlpha; } } technique fallback { pass P0 { } } ]] return Name else local Name = 'Replace' if self.Files[Name] then return Name end self.Files[Name] = [[ // SemAlpha.fx texture gTexture; technique simple { pass P0 { Texture[0] = gTexture; zWriteEnable = true; } } technique fallback { pass P0 { } } ]] return Name end end end mShader.GetReplaceTXD = function(self, Alpha) if Alpha then local Name = 'Replace_mA' if self.Files[Name] then return Name end self.Files[Name] = [[// Variables // texture gTexture0 ; float4 color = 1; // Inputs // struct PSInput { float4 Position : POSITION0; float2 TexCoord : TEXCOORD0; float4 Diffuse : COLOR0; }; // Samplers // sampler baseSampler = sampler_state { Texture = gTexture0; }; // Handlers // float4 PSHandler(PSInput PS) : COLOR0 { return tex2D(baseSampler, PS.TexCoord)*PS.Diffuse*color; } // Techniques // technique Shader_Hair_WithAlpha { pass P0 { PixelShader = compile ps_2_0 PSHandler(); AlphaBlendEnable = true; } pass P1 { zWriteEnable = false; AlphaRef = 1; DestBlend = InvSrcAlpha; DepthBias = -0.000085; } } technique fallback { pass P0 { } } ]] return Name else local Name = 'Replace_m' if self.Files[Name] then return Name end self.Files[Name] = [[// Variables // texture gTexture0 ; float4 color = 1; // Inputs // struct PSInput { float4 Position : POSITION0; float2 TexCoord : TEXCOORD0; float4 Diffuse : COLOR0; }; // Samplers // sampler baseSampler = sampler_state { Texture = gTexture0; }; // Handlers // float4 PSHandler(PSInput PS) : COLOR0 { return tex2D(baseSampler, PS.TexCoord)*PS.Diffuse*color; } // Techniques // technique Shader_Hair_NoAlpha { pass P0 { PixelShader = compile ps_2_0 PSHandler(); zWriteEnable = true; AlphaRef = 192; DepthBias = -0.000075; CullMode = None; } pass P1 { zWriteEnable = false; AlphaRef = 1; DestBlend = InvSrcAlpha; DepthBias = -0.000085; } } technique fallback { pass P0 { } } ]] return Name end end _G.mShader = mShader; for k, v in next, mShader, nil do if not _G['Shader'] then _G['Shader'] = {}; end _G['Shader'][k] = v end return mShader;