local Players = game:GetService("Players") local UIS = game:GetService("UserInputService") local RunService = game:GetService("RunService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local HttpService = game:GetService("HttpService") local LP = Players.LocalPlayer local PlayerGui = LP:WaitForChild("PlayerGui") local WavesFolder = ReplicatedStorage:WaitForChild("Waves") local WavesEnabled = ReplicatedStorage:WaitForChild("WavesEnabled") local WaveControl = ReplicatedStorage:WaitForChild("WaveControl") local WaterStreamer = script.Parent:FindFirstChild("WaterStreamer") if not WaterStreamer then for _, s in ipairs(script.Parent:GetChildren()) do if s:IsA("LocalScript") and s.Name:lower():find("waterstreamer") then WaterStreamer = s; break end end end local C = { BG = Color3.fromRGB(24,24,30), FG2 = Color3.fromRGB(30,30,38), ACC = Color3.fromRGB(40,40,52), SEL = Color3.fromRGB(90,100,255), HI = Color3.fromRGB(100,120,255), T1 = Color3.fromRGB(240,240,255), T2 = Color3.fromRGB(230,230,236), TM = Color3.fromRGB(200,200,210), BRD = Color3.fromRGB(64,64,86), KN = Color3.fromRGB(180,180,255), KNS = Color3.fromRGB(220,220,255), ERR = Color3.fromRGB(220,140,140), G1 = Color3.fromRGB(58,58,76), G2 = Color3.fromRGB(54,54,70), G3 = Color3.fromRGB(60,60,80), } local function ui(className, props, parent) local o = Instance.new(className) if props then for k,v in pairs(props) do o[k]=v end end if parent then o.Parent = parent end return o end local function roundBox(inst, r, strokeColor, t) ui("UICorner", {CornerRadius = UDim.new(0, r or 10)}, inst) if strokeColor then ui("UIStroke", {Color = strokeColor, Thickness = t or 1}, inst) end end local function btn(text, w) local b = ui("TextButton", { Text = text, AutoButtonColor = true, Size = UDim2.new(0, w or 100, 0, 28), BackgroundColor3 = C.ACC, TextColor3 = C.T1, Font = Enum.Font.GothamSemibold, TextSize = 14 }) roundBox(b, 10, Color3.fromRGB(65,65,85), 1) return b end local function makeThrottledSender(sendFn, hz) local pending, last = nil, 0 local interval = 1/(hz or 12.5) RunService.RenderStepped:Connect(function() if not pending then return end local t = os.clock() if t - last >= interval then last = t local v = pending; pending = nil sendFn(v) end end) return function(v) pending = v end end local function switch(parent, labelText) local row = ui("Frame", {Size = UDim2.new(1,0,0,34), BackgroundTransparency=1}, parent) ui("TextLabel", { Text = labelText, BackgroundTransparency = 1, TextXAlignment = Enum.TextXAlignment.Left, TextColor3 = C.T2, Font = Enum.Font.Gotham, TextSize = 14, Size = UDim2.new(1,-60,1,0) }, row) local box = ui("TextButton", { AutoButtonColor=false, Text="", AnchorPoint=Vector2.new(1,0.5), Position=UDim2.new(1,0,0.5,0), Size = UDim2.new(0,52,0,22), BackgroundColor3=C.G1 }, row) roundBox(box, 999) local knob = ui("Frame", {Size=UDim2.new(0,20,0,20), Position=UDim2.new(0,1,0,1), BackgroundColor3=C.KN}, box) roundBox(knob, 999) local on=false local function set(v) on=v box.BackgroundColor3 = v and C.HI or C.G1 knob:TweenPosition(UDim2.new(v and 1 or 0, v and -21 or 1, 0,1), "Out","Quad",0.12,true) end box.MouseButton1Click:Connect(function() set(not on) end) return {Instance=row, Set=set, Get=function() return on end, Button=box} end local function slider(parent, label, min, max, step, getFn, setFn) local row = ui("Frame", {Size=UDim2.new(1,0,0,58), BackgroundColor3=C.FG2}, parent) roundBox(row, 10, Color3.fromRGB(60,60,78), 1) ui("UIPadding", {PaddingLeft=UDim.new(0,10),PaddingRight=UDim.new(0,10),PaddingTop=UDim.new(0,8),PaddingBottom=UDim.new(0,8)}, row) ui("TextLabel", { Text=label, BackgroundTransparency=1, TextColor3=Color3.fromRGB(232,232,240), Font=Enum.Font.Gotham, TextSize=14, TextXAlignment=Enum.TextXAlignment.Left, Size=UDim2.new(1,-64,0,18) }, row) local valBtn = ui("TextButton", {Text="", BackgroundTransparency=1, TextColor3=C.TM, Font=Enum.Font.Gotham, TextSize=14, TextXAlignment=Enum.TextXAlignment.Right, AnchorPoint=Vector2.new(1,0), Position=UDim2.new(1,0,0,0), Size=UDim2.new(0,64,0,18)}, row) local bar = ui("Frame", {Size=UDim2.new(1,0,0,10), Position=UDim2.new(0,0,0,30), BackgroundColor3=C.G2}, row) roundBox(bar,5) local fill = ui("Frame", {Size=UDim2.new(0,0,1,0), BackgroundColor3=Color3.fromRGB(120,130,255)}, bar) roundBox(fill,5) local knob = ui("Frame", {Size=UDim2.new(0,14,0,14), AnchorPoint=Vector2.new(0.5,0.5), Position=UDim2.new(0,0,0.5,0), BackgroundColor3=C.KN}, bar) roundBox(knob,999); ui("UIStroke",{Color=C.KNS,Thickness=1},knob) local function snap(x) if step and step>0 then local k = math.floor((x-min)/step+0.5) return math.clamp(min + k*step, min, max) end return math.clamp(x, min, max) end local send = makeThrottledSender(function(v) setFn(v) end, 12.5) local function setVisual(v) v = snap(v) valBtn.Text = string.format("%.3f", v) local a = (v - min) / (max - min) fill.Size = UDim2.new(a,0,1,0) knob.Position = UDim2.new(a,0,0.5,0) return v end local function refresh() setVisual(getFn()) end refresh() local dragging=false local function fromX(x) local a = (x - bar.AbsolutePosition.X) / math.max(1, bar.AbsoluteSize.X) send(setVisual(min + math.clamp(a,0,1)*(max-min))) end bar.InputBegan:Connect(function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then dragging=true; fromX(i.Position.X) end end) bar.InputChanged:Connect(function(i) if dragging and (i.UserInputType==Enum.UserInputType.MouseMovement or i.UserInputType==Enum.UserInputType.Touch) then fromX(i.Position.X) end end) bar.InputEnded:Connect(function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then dragging=false end end) valBtn.MouseButton1Click:Connect(function() valBtn.AutoButtonColor=false; valBtn.Text="" local tb = ui("TextBox", { Text=tostring(getFn()), ClearTextOnFocus=false, BackgroundTransparency=0.2, BackgroundColor3=Color3.fromRGB(25,25,33), TextColor3=C.T1, Font=Enum.Font.Gotham, TextSize=14, AnchorPoint=Vector2.new(1,0), Position=UDim2.new(1,0,0,0), Size=UDim2.new(0,64,0,18) }, row) tb:CaptureFocus() tb.FocusLost:Connect(function() local nv = tonumber(tb.Text); if nv then send(setVisual(nv)) end tb:Destroy(); valBtn.AutoButtonColor=true; refresh() end) end) return {Refresh=refresh} end local function dirPad(parent, getX, getY, setX, setY) local root = ui("Frame", {Size=UDim2.new(1,0,0,120), BackgroundTransparency=1}, parent) ui("TextLabel", {Text="Direction (X,Y)", BackgroundTransparency=1, TextColor3=Color3.fromRGB(232,232,240), Font=Enum.Font.Gotham, TextSize=14, TextXAlignment=Enum.TextXAlignment.Left, Size=UDim2.new(1,0,0,18)}, root) local pad = ui("Frame", {Size=UDim2.new(0,120,0,120), Position=UDim2.new(0,0,0,24), BackgroundColor3=Color3.fromRGB(30,30,40)}, root) roundBox(pad,60, Color3.fromRGB(64,64,84), 1) ui("Frame",{Size=UDim2.new(0,2,1,-12), Position=UDim2.new(0.5,-1,0,6), BackgroundColor3=Color3.fromRGB(70,70,96)}, pad) ui("Frame",{Size=UDim2.new(1,-12,0,2), Position=UDim2.new(0,6,0.5,-1), BackgroundColor3=Color3.fromRGB(70,70,96)}, pad) local knob = ui("Frame",{Size=UDim2.new(0,16,0,16), AnchorPoint=Vector2.new(0.5,0.5), Position=UDim2.new(0.5,0,0.5,0), BackgroundColor3=C.KN}, pad) roundBox(knob,999); ui("UIStroke",{Color=C.KNS,Thickness=1},knob) local coord = ui("TextLabel",{Text="", BackgroundTransparency=1, TextColor3=C.TM, Font=Enum.Font.Code, TextSize=13, AnchorPoint=Vector2.new(1,0), Position=UDim2.new(1,0,0,2), Size=UDim2.new(0,140,0,16), TextXAlignment=Enum.TextXAlignment.Right}, root) local function setVisual(dx,dy) local m = math.sqrt(dx*dx+dy*dy); if m>1 then dx,dy=dx/m,dy/m end local r = math.min(pad.AbsoluteSize.X, pad.AbsoluteSize.Y)*0.5 - 8 if r<=0 then knob.Position = UDim2.new(0.5,0,0.5,0) else local cx,cy=pad.AbsolutePosition.X+pad.AbsoluteSize.X*0.5, pad.AbsolutePosition.Y+pad.AbsoluteSize.Y*0.5 local kx,ky=cx+dx*r, cy-dy*r local rx = (kx-pad.AbsolutePosition.X)/math.max(1,pad.AbsoluteSize.X) local ry = (ky-pad.AbsolutePosition.Y)/math.max(1,pad.AbsoluteSize.Y) knob.Position = UDim2.new(rx,0,ry,0) end coord.Text = string.format("(%.3f, %.3f)", dx, dy) end local dragging=false local function applyFrom(px,py) local ax,ay = pad.AbsolutePosition.X, pad.AbsolutePosition.Y local cx,cy = ax+pad.AbsoluteSize.X*0.5, ay+pad.AbsoluteSize.Y*0.5 local dx,dy = (px-cx), -(py-cy) local r = math.min(pad.AbsoluteSize.X, pad.AbsoluteSize.Y)*0.5 - 8 if r<=0 then return end local vx,vy = dx/r, dy/r local l = math.sqrt(vx*vx+vy*vy); if l>1 then vx,vy=vx/l,vy/l end setX(vx); setY(vy); setVisual(vx,vy) end pad.InputBegan:Connect(function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then dragging=true; applyFrom(i.Position.X,i.Position.Y) end end) pad.InputChanged:Connect(function(i) if dragging and (i.UserInputType==Enum.UserInputType.MouseMovement or i.UserInputType==Enum.UserInputType.Touch) then applyFrom(i.Position.X,i.Position.Y) end end) pad.InputEnded:Connect(function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then dragging=false end end) RunService.Heartbeat:Once(function() setVisual(getX(), getY()) end) return {Refresh=function() setVisual(getX(), getY()) end} end local function waveCount() local n=0; while WavesFolder:FindFirstChild("Wave"..(n+1)) do n+=1 end; return n end local function setServerWave(i, prop, v) WaveControl:FireServer("set", i, prop, v) end local gui = ui("ScreenGui", {Name="GerstnerPanel", ResetOnSpawn=false, IgnoreGuiInset=true}, PlayerGui) local panel = ui("Frame", {Size=UDim2.new(0,420,0,520), Position=UDim2.new(0,24,0,120), BackgroundColor3=C.BG, BorderSizePixel=0}, gui) roundBox(panel,14, C.BRD, 1) local header = ui("Frame",{Size=UDim2.new(1,0,0,40), BackgroundTransparency=1}, panel) local title = ui("TextLabel", { Text="Gerstner Waves — Sandbox", BackgroundTransparency=1, TextColor3=C.T1, Font=Enum.Font.GothamBold, TextSize=18, TextXAlignment=Enum.TextXAlignment.Left, Position=UDim2.new(0,16,0,0), Size=UDim2.new(1,-160,1,0) }, header) local bMin = btn("—", 36); bMin.Parent=header; bMin.Position=UDim2.new(1,-96,0,6) local bX = btn("X", 36); bX.Parent=header; bX.Position=UDim2.new(1,-52,0,6) do local dragging=false; local off=Vector2.new() local function mouse() local p=UIS:GetMouseLocation(); return Vector2.new(p.X,p.Y) end header.InputBegan:Connect(function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 then dragging=true; off=mouse()-panel.AbsolutePosition end end) header.InputEnded:Connect(function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 then dragging=false end end) UIS.InputChanged:Connect(function(i) if dragging and (i.UserInputType==Enum.UserInputType.MouseMovement or i.UserInputType==Enum.UserInputType.Touch) then local m=mouse(); panel.Position = UDim2.fromOffset(m.X-off.X, m.Y-off.Y) end end) end bMin.MouseButton1Click:Connect(function() local h=panel.Size.Y.Offset panel:TweenSize(UDim2.new(0,420,0,(h<=50) and 520 or 48), "Out","Quad",0.15,true) end) bX.MouseButton1Click:Connect(function() panel.Visible=false end) UIS.InputBegan:Connect(function(i,gp) if not gp and i.KeyCode==Enum.KeyCode.F6 then panel.Visible = not panel.Visible end end) local topBar = ui("Frame",{BackgroundTransparency=1, Size=UDim2.new(1,-16,0,32), Position=UDim2.new(0,8,0,46)}, panel) local bToggle = btn("On/Off"); bToggle.Parent=topBar; bToggle.Position=UDim2.new(0,0,0,0) local bReset = btn("Réinitialiser"); bReset.Parent=topBar; bReset.Position=UDim2.new(0,112,0,0) bToggle.MouseButton1Click:Connect(function() WaveControl:FireServer("toggle") end) bReset.MouseButton1Click:Connect(function() WaveControl:FireServer("reset") end) local tabBar = ui("Frame",{BackgroundTransparency=1, Size=UDim2.new(1,-16,0,32), Position=UDim2.new(0,8,0,86)}, panel) local TABS = {"Global","Vagues","Presets","Paramètres"} local tabBtn, current = {}, "Vagues" for i,name in ipairs(TABS) do local b=btn(name,100); b.Parent=tabBar; b.Position=UDim2.new(0,(i-1)*106,0,0); tabBtn[name]=b end local pages = ui("Frame",{Size=UDim2.new(1,-16,1,-140), Position=UDim2.new(0,8,0,124), BackgroundTransparency=1}, panel) local function clearPages() for _,c in ipairs(pages:GetChildren()) do if c:IsA("Frame") then c:Destroy() end end end local function pageGlobal() local root = ui("Frame",{BackgroundTransparency=1, Size=UDim2.fromScale(1,1)}, pages) local sc = ui("ScrollingFrame",{BackgroundTransparency=1, Size=UDim2.fromScale(1,1), ScrollBarThickness=8, AutomaticCanvasSize=Enum.AutomaticSize.Y}, root) ui("UIListLayout",{Padding=UDim.new(0,10), SortOrder=Enum.SortOrder.LayoutOrder}, sc) ui("TextLabel",{ Text=("Vagues actives : %d"):format(waveCount()), TextColor3=C.T2, BackgroundTransparency=1, Font=Enum.Font.GothamSemibold, TextSize=16, Size=UDim2.new(1,0,0,24) }, sc) local sw = switch(sc, "Activer les vagues") sw.Set(WavesEnabled.Value) WavesEnabled.Changed:Connect(function() sw.Set(WavesEnabled.Value) end) sw.Button.MouseButton1Click:Connect(function() WaveControl:FireServer("toggle") end) local br = btn("Réinitialiser toutes les vagues", 240); br.Parent=sc br.MouseButton1Click:Connect(function() WaveControl:FireServer("reset") end) return root end local function makeWaveSliders(parent, idx, waveFolder) local function gv(p) return waveFolder[p].Value end local function sv(p,v) setServerWave(idx,p,v) end local rf = {} table.insert(rf, slider(parent,"Amplitude", 0,100,0.1, function() return gv("Amplitude") end, function(v) sv("Amplitude",v) end ).Refresh) table.insert(rf, slider(parent,"Longueur d'onde", 0.1,1000,0.1, function() return gv("Wavelength") end, function(v) sv("Wavelength",v) end ).Refresh) table.insert(rf, slider(parent,"Vitesse", 0,50,0.01, function() return gv("Speed") end, function(v) sv("Speed",v) end ).Refresh) table.insert(rf, slider(parent,"Raideur (Steepness)", 0,1,0.001, function() return gv("Steepness") end, function(v) sv("Steepness",v) end ).Refresh) local dp = dirPad(parent, function() return gv("DirectionX") end, function() return gv("DirectionY") end, function(v) sv("DirectionX",v) end, function(v) sv("DirectionY",v) end) table.insert(rf, dp.Refresh) local bar = ui("Frame",{Size=UDim2.new(1,0,0,32), BackgroundTransparency=1}, parent) local bN = btn("Norm. direction", 150); bN.Parent=bar bN.MouseButton1Click:Connect(function() local dx,dy=gv("DirectionX"),gv("DirectionY"); local m=math.sqrt(dx*dx+dy*dy) if m<1e-6 then dx,dy=1,0 else dx,dy=dx/m,dy/m end sv("DirectionX",dx); sv("DirectionY",dy) end) local bR = btn("Randomiser",120); bR.Parent=bar; bR.Position=UDim2.new(0,160,0,0) bR.MouseButton1Click:Connect(function() local function rnd(a,b) return a + math.random()*(b-a) end sv("Amplitude", rnd(0.1,5)); sv("Wavelength", rnd(6,24)); sv("Speed", rnd(0.5,3.2)); sv("Steepness", rnd(0.05,0.5)) local a=rnd(0, math.pi*2); sv("DirectionX", math.cos(a)); sv("DirectionY", math.sin(a)) end) local conns={} for _,v in ipairs(waveFolder:GetChildren()) do if v:IsA("NumberValue") then table.insert(conns, v.Changed:Connect(function() for _,f in ipairs(rf) do f() end end)) end end return function() for _,c in ipairs(conns) do if c.Disconnect then c:Disconnect() end end end end local function pageWaves() local root = ui("Frame",{BackgroundTransparency=1, Size=UDim2.fromScale(1,1)}, pages) local left = ui("Frame",{BackgroundTransparency=1, Size=UDim2.new(0,112,1,0)}, root) local right= ui("ScrollingFrame",{BackgroundTransparency=1, Size=UDim2.new(1,-120,1,0), Position=UDim2.new(0,120,0,0), ScrollBarThickness=8, AutomaticCanvasSize=Enum.AutomaticSize.Y}, root) ui("UIListLayout",{Padding=UDim.new(0,10), SortOrder=Enum.SortOrder.LayoutOrder}, right) local selected=1; local buttons={}; local cleanup=function()end local function select(i) selected=i for k,b in pairs(buttons) do b.BackgroundColor3 = (k==i) and C.SEL or C.ACC end for _,c in ipairs(right:GetChildren()) do if c:IsA("Frame") then c:Destroy() end end cleanup() local wf=WavesFolder:FindFirstChild("Wave"..i) if not wf then return end cleanup = makeWaveSliders(right, i, wf) end for i=1, waveCount() do local b=btn(("Vague %d"):format(i),100); b.Parent=left; b.Position=UDim2.new(0,6,0,(i-1)*36) buttons[i]=b; b.MouseButton1Click:Connect(function() select(i) end) end select(1) return root end local PRESETS = { ["Calme"] = { {A=0.2, L=18, S=1.0, Q=0.15, dir=0.0}, {A=0.15,L=24, S=0.9, Q=0.12, dir=0.7}, {A=0.12,L=12, S=1.4, Q=0.18, dir=2.1}, }, ["Clapot"] = { {A=0.6, L=10, S=2.0, Q=0.35, dir=0.2}, {A=0.3, L=7, S=2.6, Q=0.45, dir=1.3}, {A=0.25,L=6, S=2.8, Q=0.40, dir=2.6}, }, ["Swell diagonal"] = { {A=0.8, L=22, S=1.3, Q=0.25, dir=math.rad(35)}, {A=0.3, L=12, S=2.4, Q=0.35, dir=math.rad(205)}, {A=0.25,L=18, S=1.9, Q=0.28, dir=math.rad(110)}, }, ["Verre (glass)"] = { {A=0.05,L=28, S=0.7, Q=0.10, dir=0.0}, {A=0.04,L=32, S=0.6, Q=0.08, dir=1.4}, {A=0.03,L=20, S=1.1, Q=0.10, dir=2.7}, }, ["Tempête"] = { {A=1.8, L=14, S=2.6, Q=0.45, dir=0.3}, {A=1.1, L=10, S=3.0, Q=0.50, dir=1.6}, {A=0.7, L=7, S=3.3, Q=0.50, dir=2.9}, }, } local function applyPreset(name) local p = PRESETS[name]; if not p then return end local n = math.min(#p, waveCount()) for i=1,n do local w=p[i] task.defer(function() setServerWave(i,"Amplitude", w.A) end) task.defer(function() setServerWave(i,"Wavelength",w.L) end) task.defer(function() setServerWave(i,"Speed", w.S) end) task.defer(function() setServerWave(i,"Steepness", w.Q) end) task.defer(function() setServerWave(i,"DirectionX", math.cos(w.dir)) setServerWave(i,"DirectionY", math.sin(w.dir)) end) end end local function wavesToJSON() local arr={} for i=1, waveCount() do local wf=WavesFolder:FindFirstChild("Wave"..i) arr[i] = { Amplitude = wf.Amplitude.Value, Wavelength= wf.Wavelength.Value, Speed = wf.Speed.Value, Steepness = wf.Steepness.Value, DirectionX= wf.DirectionX.Value, DirectionY= wf.DirectionY.Value, } end return HttpService:JSONEncode(arr) end local function importJSON(txt) local ok, arr = pcall(function() return HttpService:JSONDecode(txt) end) if not ok or type(arr)~="table" then return false end local n = math.min(#arr, waveCount()) for i=1,n do local w=arr[i]; if type(w)=="table" then local need={"Amplitude","Wavelength","Speed","Steepness","DirectionX","DirectionY"} for _,k in ipairs(need) do if w[k]==nil then return false end end for k,v in pairs(w) do setServerWave(i,k, tonumber(v)) end local dx,dy= w.DirectionX, w.DirectionY local m = math.sqrt(dx*dx+dy*dy) if m>1e-6 then setServerWave(i,"DirectionX", dx/m) setServerWave(i,"DirectionY", dy/m) end end end return true end local function pagePresets() local root = ui("Frame",{BackgroundTransparency=1, Size=UDim2.fromScale(1,1)}, pages) local grid = ui("Frame",{BackgroundTransparency=1, Size=UDim2.new(1,0,0,140)}, root) ui("UIGridLayout",{CellPadding=UDim2.new(0,10,0,10), CellSize=UDim2.new(0,150,0,34), FillDirectionMaxCells=3}, grid) for name in pairs(PRESETS) do local b=btn(name,150); b.Parent=grid; b.MouseButton1Click:Connect(function() applyPreset(name) end) end local tb = ui("TextBox",{ ClearTextOnFocus=false, MultiLine=true, Text="", PlaceholderText="Colle ici le JSON des vagues…", TextXAlignment=Enum.TextXAlignment.Left, TextYAlignment=Enum.TextYAlignment.Top, Font=Enum.Font.Code, TextSize=14, TextColor3=C.T1, BackgroundColor3=Color3.fromRGB(22,22,28), Size=UDim2.new(1,0,1,-160), Position=UDim2.new(0,0,0,150) }, root) roundBox(tb,10, C.G3,1) ui("UIPadding",{PaddingLeft=UDim.new(0,8),PaddingRight=UDim.new(0,8),PaddingTop=UDim.new(0,8),PaddingBottom=UDim.new(0,8)}, tb) local bar = ui("Frame",{BackgroundTransparency=1, Size=UDim2.new(1,0,0,34), Position=UDim2.new(0,0,1,-34)}, root) local bE = btn("Exporter JSON",150); bE.Parent=bar; bE.MouseButton1Click:Connect(function() tb.Text = wavesToJSON() end) local bI = btn("Importer JSON",150); bI.Parent=bar; bI.Position=UDim2.new(0,160,0,0) bI.MouseButton1Click:Connect(function() tb.Text = importJSON(tb.Text) and "-- Import OK --" or "-- JSON invalide --" end) return root end local RMIN, RMAX, RSTEP = 50, 2000, 10 local function getRD() local d=300 if WaterStreamer and WaterStreamer:GetAttribute("RenderDistance") then d = WaterStreamer:GetAttribute("RenderDistance") end return d end local function setRD(v) v = math.clamp(math.floor(v/RSTEP+0.5)*RSTEP, RMIN, RMAX) if WaterStreamer then WaterStreamer:SetAttribute("RenderDistance", v) end end local function pageSettings() local root = ui("Frame",{BackgroundTransparency=1, Size=UDim2.fromScale(1,1)}, pages) local sc = ui("ScrollingFrame",{BackgroundTransparency=1, Size=UDim2.fromScale(1,1), ScrollBarThickness=8, AutomaticCanvasSize=Enum.AutomaticSize.Y}, root) ui("UIListLayout",{Padding=UDim.new(0,10), SortOrder=Enum.SortOrder.LayoutOrder}, sc) slider(sc, "Distance d'affichage (studs)", RMIN, RMAX, RSTEP, getRD, setRD) if WaterStreamer then WaterStreamer:GetAttributeChangedSignal("RenderDistance"):Connect(function() end) end return root end local builders = {["Global"]=pageGlobal, ["Vagues"]=pageWaves, ["Presets"]=pagePresets, ["Paramètres"]=pageSettings} local function openTab(name) current=name for n,b in pairs(tabBtn) do b.BackgroundColor3 = (n==name) and C.SEL or C.ACC end clearPages() builders[name]() end for n,b in pairs(tabBtn) do b.MouseButton1Click:Connect(function() openTab(n) end) end openTab(current) local function syncEnabled() local on=WavesEnabled.Value title.Text = on and "Gerstner Waves — Sandbox (ON)" or "Gerstner Waves — Sandbox (OFF)" title.TextColor3 = on and C.T1 or C.ERR end syncEnabled(); WavesEnabled.Changed:Connect(syncEnabled)