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)