@adiholick/nvstudio-mcp 2.2.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +57 -47
  2. package/agent-plugin/mcp_config.json +8 -0
  3. package/agent-plugin/plugin.json +6 -0
  4. package/agent-plugin/rules/AGENTS.md +118 -0
  5. package/agent-plugin/skills/nvstudio-animation-vfx/SKILL.md +120 -0
  6. package/agent-plugin/skills/nvstudio-assets-terrain-guide/SKILL.md +27 -0
  7. package/agent-plugin/skills/nvstudio-gui-standards/SKILL.md +419 -0
  8. package/agent-plugin/skills/nvstudio-mcp-guide/SKILL.md +83 -0
  9. package/agent-plugin/skills/nvstudio-rig-engine/SKILL.md +170 -0
  10. package/agent-plugin/skills/nvstudio-roblox-sync-guide/SKILL.md +16 -0
  11. package/dist/build-plugin.js +55 -0
  12. package/dist/index.js +841 -3
  13. package/dist/luau-validator.js +99 -34
  14. package/dist/roblox-bridge.js +152 -4
  15. package/dist/roblox-docs.js +264 -0
  16. package/dist/vfx-database.js +507 -0
  17. package/package.json +6 -4
  18. package/public/index.html +1590 -180
  19. package/studio-plugin/Main.server.lua +1 -1
  20. package/studio-plugin/animate_character.lua +36 -0
  21. package/studio-plugin/apply_vfx.lua +59 -0
  22. package/studio-plugin/batch_create_instances.lua +128 -0
  23. package/studio-plugin/batch_set_properties.lua +69 -0
  24. package/studio-plugin/edit_rig.lua +35 -0
  25. package/studio-plugin/insert_asset.lua +85 -12
  26. package/studio-plugin/nv_anim_dispatcher.lua +239 -0
  27. package/studio-plugin/nv_auto_rigger.lua +392 -0
  28. package/studio-plugin/nv_curves.lua +260 -0
  29. package/studio-plugin/nv_joint_math.lua +138 -0
  30. package/studio-plugin/nv_kinematics.lua +248 -0
  31. package/studio-plugin/nv_motion_generator.lua +279 -0
  32. package/studio-plugin/nv_rig_dispatcher.lua +319 -0
  33. package/studio-plugin/nv_vfx_serializer.lua +363 -0
  34. package/studio-plugin/nvstudio_mcp.rbxmx +3824 -60
  35. package/studio-plugin/preview_asset.lua +99 -0
  36. package/studio-plugin/scan_security.lua +81 -0
  37. package/studio-plugin/scan_vfx.lua +85 -0
  38. package/studio-plugin/scrub_timeline.lua +122 -0
  39. package/studio-plugin/security_scanner.lua +346 -0
  40. package/studio-plugin/set_properties.lua +83 -0
  41. package/studio-plugin/solve_limb_ik.lua +63 -0
  42. package/studio-plugin/ui_manager.lua +1 -1
@@ -0,0 +1,419 @@
1
+ ---
2
+ name: nvstudio-gui-standards
3
+ description: Panduan standar arsitektur dan taste desain StarterGui Roblox (Struktur Instance, Smart Device Detection NVSelfTitle/NVDance, Layout Hierarchy, dan NV Dark Modern Aesthetics).
4
+ ---
5
+
6
+ # NVStudio Roblox GUI Design Standards & Taste Guide
7
+
8
+ Gunakan skill ini sebagai acuan mutlak setiap kali membuat atau memodifikasi GUI di Roblox Studio (`StarterGui`). Semua GUI dirancang mengikuti filosofi arsitektur **NV-Series** (`NVMembership`, `NVMusic`, `NVSelfTitle`, `NVServer`, `NVDance`).
9
+
10
+ ---
11
+
12
+ ## 1. Aturan Fundamental Arsitektur GUI
13
+
14
+ 1. **Roblox Instance Penuh (Bukan Pure Script):**
15
+ - GUI dibuat sebagai hierarki **Instance nyata di Roblox Studio** (menggunakan MCP `create_instance` / `set_properties` atau builder helper Luau).
16
+ - DILARANG membuat GUI murni on-the-fly dari script kosong dengan ratusan baris `Instance.new`.
17
+ - Script Luau (`LocalScript` / `ModuleScript`) hanya bertindak sebagai **UI Controller** yang mengontrol state, event click/input, tweening, dan integrasi server.
18
+
19
+ 2. **Hierarki ScreenGui & MainFrame:**
20
+ - Setiap GUI berada di bawah `ScreenGui` dengan properti:
21
+ - `ResetOnSpawn = false`
22
+ - `ZIndexBehavior = Enum.ZIndexBehavior.Sibling`
23
+ - `IgnoreGuiInset = false`
24
+ - Child utama dari `ScreenGui` WAJIB berupa Frame bernama **`MainFrame`** (atau `ContainerFrame` yang membungkus `MainFrame`).
25
+ - `MainFrame.AnchorPoint` default: `Vector2.new(0.5, 0.5)` dengan posisi tengah `UDim2.new(0.5, 0, 0.5, 0)`.
26
+
27
+ 3. **Aturan Wajib Layout Frame:**
28
+ - **Setiap Frame** WAJIB memiliki:
29
+ - `UIListLayout` (atau `UIGridLayout` jika grid konten) dengan `SortOrder = Enum.SortOrder.LayoutOrder`.
30
+ - `UIPadding` (memberikan padding konsisten agar elemen tidak menempel ke tepi).
31
+ - **Setiap Item di dalam Frame** WAJIB memiliki properti `LayoutOrder` eksplisit (0, 1, 2, 3...) agar susunan elemen teratur secara deterministik.
32
+
33
+ 4. **Aturan Wajib ScrollingFrame:**
34
+ - Setiap `ScrollingFrame` WAJIB memiliki Frame transparan pembatas bernama **`Spacer`** atau **`SPACER`**:
35
+ - `Name = "Spacer"`
36
+ - `BackgroundTransparency = 1`
37
+ - `BorderSizePixel = 0`
38
+ - `LayoutOrder = 99999` (atau `999999999`)
39
+ - Size: `UDim2.new(1, 0, 0.15, 0)` atau `UDim2.new(0, 0, 0, 20)` (menyesuaikan orientasi scroll vertical/horizontal).
40
+ - Ini memastikan elemen paling bawah/ujung tidak terpotong saat pemain melakukan scroll.
41
+
42
+ 5. **Pembangun Deklaratif Cepat (`batch_create_instances`):**
43
+ - Sangat disarankan membangun seluruh pohon GUI menggunakan tool `batch_create_instances` dengan mode `tree` bersarang.
44
+ - Contoh payload tree:
45
+ ```json
46
+ {
47
+ "target": "StarterGui.MyScreen",
48
+ "tree": {
49
+ "className": "Frame",
50
+ "name": "MainFrame",
51
+ "properties": {
52
+ "Size": [0.5, 0, 0.6, 0],
53
+ "Position": [0.5, 0, 0.5, 0],
54
+ "AnchorPoint": [0.5, 0.5],
55
+ "BackgroundColor3": [28, 28, 30]
56
+ },
57
+ "children": [
58
+ { "className": "UICorner", "properties": { "CornerRadius": [0, 10] } },
59
+ { "className": "UIStroke", "properties": { "Color": [63, 63, 63], "Thickness": 1 } },
60
+ { "className": "UIListLayout", "properties": { "SortOrder": "LayoutOrder" } },
61
+ { "className": "UIPadding", "properties": { "PaddingTop": [0, 10], "PaddingBottom": [0, 10] } }
62
+ ]
63
+ }
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## 2. Smart Device Detection & Responsive Scaling System
70
+
71
+ Diadopsi langsung dari **`NVSelfTitle`** dan **`NVDance`**. Sistem ini membedakan ukuran perangkat secara akurat dan memisahkan skala PC, Mobile, Tablet, dan Console secara independen.
72
+
73
+ ### Modul Logika Deteksi Perangkat:
74
+ ```lua
75
+ local GuiService = game:GetService("GuiService")
76
+ local UserInputService = game:GetService("UserInputService")
77
+ local Workspace = game:GetService("Workspace")
78
+
79
+ -- [KONFIGURASI SKALA INDEPENDEN PER PERANGKAT]
80
+ local PC_Scale = 1.0 -- Skala normal PC / Laptop
81
+ local PC_Portrait_Scale = 1.2 -- Jendela PC orientasi portrait
82
+ local Mobile_Landscape_Scale = 1.45 -- Smartphone landscape
83
+ local Mobile_Portrait_Scale = 1.7 -- Smartphone portrait
84
+ local Tablet_Landscape_Scale = 1.25 -- Tablet / iPad landscape
85
+ local Tablet_Portrait_Scale = 1.45 -- Tablet / iPad portrait
86
+ local Console_Scale = 1.15 -- PlayStation / Xbox / TV
87
+ local GlobalScaleMultiplier = 1.0
88
+
89
+ -- Fungsi Cerdas Deteksi Perangkat (NVSelfTitle / NVDance Standard)
90
+ local function GetDeviceType(vw: number, vh: number, mainFrame: GuiObject): string
91
+ -- 1. Opsi override untuk testing di Studio lewat Attribute "ForceDevice" pada MainFrame:
92
+ local forceDevice = mainFrame:GetAttribute("ForceDevice")
93
+ if forceDevice and (forceDevice == "Phone" or forceDevice == "Tablet" or forceDevice == "PC" or forceDevice == "Console") then
94
+ return forceDevice :: string
95
+ end
96
+
97
+ -- 2. Console (PlayStation / Xbox / Smart TV)
98
+ if GuiService:IsTenFootInterface() then
99
+ return "Console"
100
+ end
101
+
102
+ -- 3. Touch Device (Smartphone vs Tablet)
103
+ -- Touchscreen aktif dan keyboard fisik tidak aktif
104
+ if UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled then
105
+ local smallerDimension = math.min(vw, vh)
106
+ -- Dimensi logika layar smartphone di Roblox umumnya < 550 pixel, Tablet >= 550 pixel
107
+ if smallerDimension > 0 and smallerDimension < 550 then
108
+ return "Phone"
109
+ else
110
+ return "Tablet"
111
+ end
112
+ end
113
+
114
+ -- 4. Default: Desktop / PC / Laptop
115
+ return "PC"
116
+ end
117
+ ```
118
+
119
+ ### Handler Update Skala & Event Listener:
120
+ ```lua
121
+ local function UpdateMainFrameScale(mainFrame: Frame, defaultSize: UDim2, isOpen: boolean)
122
+ local camera = Workspace.CurrentCamera
123
+ if not camera then return end
124
+
125
+ local vw, vh = camera.ViewportSize.X, camera.ViewportSize.Y
126
+ local isPortrait = vh > vw
127
+ local deviceType = GetDeviceType(vw, vh, mainFrame)
128
+
129
+ local baseScale = 1.0
130
+ if deviceType == "Phone" then
131
+ baseScale = isPortrait and Mobile_Portrait_Scale or Mobile_Landscape_Scale
132
+ elseif deviceType == "Tablet" then
133
+ baseScale = isPortrait and Tablet_Portrait_Scale or Tablet_Landscape_Scale
134
+ elseif deviceType == "Console" then
135
+ baseScale = Console_Scale
136
+ else
137
+ baseScale = isPortrait and PC_Portrait_Scale or PC_Scale
138
+ end
139
+
140
+ local totalScale = GlobalScaleMultiplier * baseScale
141
+
142
+ mainFrame.Size = UDim2.new(
143
+ defaultSize.X.Scale * totalScale,
144
+ defaultSize.X.Offset * totalScale,
145
+ defaultSize.Y.Scale * totalScale,
146
+ defaultSize.Y.Offset * totalScale
147
+ )
148
+
149
+ if isOpen then
150
+ mainFrame.Position = UDim2.new(0.5, 0, 0.5, 0)
151
+ end
152
+ end
153
+ ```
154
+
155
+ ### Studio Attributes Testing Support:
156
+ Pasang Attribute pada `MainFrame`:
157
+ - `ForceDevice` (`String`: `"Phone"`, `"Tablet"`, `"PC"`, `"Console"`)
158
+ - `PCScale` (`Number`)
159
+ - `MobileScale` (`Number`)
160
+
161
+ ---
162
+
163
+ ## 3. NV Dark Modern Taste & Design Tokens
164
+
165
+ Semua GUI NV-Series menggunakan tema gelap elegan (*business-grade dark mode*) dengan kontras tinggi dan sentuhan rounded halus.
166
+
167
+ | Komponen | Token Warna / Properti | Nilai RGB / Hex | Kegunaan |
168
+ | :--- | :--- | :--- | :--- |
169
+ | **MainFrame Background** | `BackgroundColor3` | `Color3.fromRGB(28, 28, 30)` (`#1C1C1E`) | Latar belakang frame utama jendela |
170
+ | **Card / Item Background** | `BackgroundColor3` | `Color3.fromRGB(25, 25, 25)` (`#191919`) | Latar belakang list item, segment container |
171
+ | **Sub-Frame / Input** | `BackgroundColor3` | `Color3.fromRGB(13, 13, 13)` (`#0D0D0D`) | Textbox search, area preview, inner box |
172
+ | **Border / UIStroke** | `Color`, `Thickness = 1` | `Color3.fromRGB(63, 63, 63)` (`#3F3F3F`) | Garis tepi halus (`ApplyStrokeMode.Border`) |
173
+ | **Active Accent (Cyan)** | `Color3` | `Color3.fromRGB(0, 170, 255)` / `fromRGB(0, 255, 255)` | Tab aktif, border seleksi (`SelectStroke`) |
174
+ | **Text Primary** | `TextColor3` | `Color3.fromRGB(255, 255, 255)` (`#FFFFFF`) | Judul, label penting, nama item |
175
+ | **Text Secondary** | `TextColor3` | `Color3.fromRGB(163, 162, 165)` (`#A3A2A5`) | Deskripsi, subtitle, teks tidak aktif |
176
+
177
+ ### Corner Radius (`UICorner`):
178
+ - `MainFrame`: `UDim.new(0, 10)` atau `UDim.new(0.03, 0)`
179
+ - `Card / Container Frame`: `UDim.new(0, 8)` atau `UDim.new(0, 10)`
180
+ - `Buttons / Pills / Tags`: `UDim.new(0.2, 0)` atau `UDim.new(0.5, 0)`
181
+
182
+ ### Tipografi (`Font`):
183
+ - **Judul & Header:** `Enum.Font.GothamBold`
184
+ - **Teks Tubuh & List:** `Enum.Font.Gotham` atau `Enum.Font.GothamMedium`
185
+ - **Aksen / Badge:** `Enum.Font.FredokaOne` atau `Enum.Font.SourceSansBold`
186
+
187
+ ---
188
+
189
+ ## 4. UI Controller Script Template (LocalScript)
190
+
191
+ Setiap ScreenGui memiliki Controller (`LocalScript`) dengan struktur standar berikut:
192
+
193
+ ```lua
194
+ --!strict
195
+ local Players = game:GetService("Players")
196
+ local ReplicatedStorage = game:GetService("ReplicatedStorage")
197
+ local TweenService = game:GetService("TweenService")
198
+ local UserInputService = game:GetService("UserInputService")
199
+ local GuiService = game:GetService("GuiService")
200
+ local Workspace = game:GetService("Workspace")
201
+
202
+ local Modules = ReplicatedStorage:WaitForChild("Modules")
203
+ local Maid = require(Modules:WaitForChild("Maid"))
204
+ local Icon = require(ReplicatedStorage:WaitForChild("Icon")) -- TopbarPlus
205
+
206
+ local GUI = script.Parent
207
+ local MainFrame = GUI:WaitForChild("MainFrame") :: Frame
208
+ local ScrollingFrame = MainFrame:FindFirstChildWhichIsA("ScrollingFrame", true)
209
+
210
+ local Controller = {
211
+ _maid = Maid.new(),
212
+ IsOpen = false,
213
+ DefaultSize = MainFrame.Size,
214
+ }
215
+
216
+ -- [1] ANIMASI TOGGLE WINDOW (Pop-In & Slide)
217
+ function Controller:Toggle(state: boolean)
218
+ self.IsOpen = state
219
+ local tweenInfo = TweenInfo.new(0.4, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
220
+ local targetPos = state and UDim2.new(0.5, 0, 0.5, 0) or UDim2.new(0.5, 0, -0.6, 0)
221
+
222
+ if state then
223
+ MainFrame.Visible = true
224
+ UpdateMainFrameScale(MainFrame, self.DefaultSize, true)
225
+ end
226
+
227
+ local tween = TweenService:Create(MainFrame, tweenInfo, { Position = targetPos })
228
+ tween:Play()
229
+
230
+ if not state then
231
+ tween.Completed:Connect(function()
232
+ if not self.IsOpen then
233
+ MainFrame.Visible = false
234
+ end
235
+ end)
236
+ end
237
+ end
238
+
239
+ -- [2] SELEKSI TOMBOL INTERAKTIF (Pola SelectStroke)
240
+ function Controller:HighlightButton(parentContainer: Instance, activeBtn: Instance?)
241
+ for _, child in ipairs(parentContainer:GetChildren()) do
242
+ if child:IsA("TextButton") or child:IsA("ImageButton") then
243
+ local stroke = child:FindFirstChild("SelectStroke") :: UIStroke?
244
+ if not stroke then
245
+ stroke = Instance.new("UIStroke")
246
+ stroke.Name = "SelectStroke"
247
+ stroke.Thickness = 1
248
+ stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
249
+ stroke.Parent = child
250
+ end
251
+ if child == activeBtn then
252
+ stroke.Color = Color3.fromRGB(0, 255, 255)
253
+ else
254
+ stroke.Color = Color3.fromRGB(60, 60, 65)
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ -- [3] SETUP TOPBARPLUS ICON
261
+ local topbarIcon = Icon.new()
262
+ topbarIcon:setLabel(GUI.Name)
263
+ topbarIcon.toggled:Connect(function(isSelected: boolean)
264
+ Controller:Toggle(isSelected)
265
+ end)
266
+
267
+ -- [4] HOOK CAMERA & ATTRIBUTES
268
+ local function hookCamera()
269
+ local cam = Workspace.CurrentCamera
270
+ if cam then
271
+ cam:GetPropertyChangedSignal("ViewportSize"):Connect(function()
272
+ UpdateMainFrameScale(MainFrame, Controller.DefaultSize, Controller.IsOpen)
273
+ end)
274
+ UpdateMainFrameScale(MainFrame, Controller.DefaultSize, Controller.IsOpen)
275
+ end
276
+ end
277
+ hookCamera()
278
+ Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(hookCamera)
279
+ ```
280
+
281
+ ---
282
+
283
+ ## 5. Komponen Interaktif Standar NV-Series
284
+
285
+ ### A. Vertical Slider (`VerticalSlider`)
286
+ Diadopsi dari `NVMusic` (Volume Fader Master, Deck A/B Volume, Speed Control):
287
+ - **Struktur Instance:**
288
+ - `SliderFrame` (Frame Track Vertikal: Width `0.12` - `0.2`, Height `0.85`, background `#101010`, `UICorner`, `UIStroke`, `UIPadding`).
289
+ - `Button` (Draggable Knob: AnchorPoint `0.5, 0.5`, `UICorner`).
290
+ - `ValueTextTarget` (TextLabel indikator nilai).
291
+ - **Logika Luau:**
292
+ ```lua
293
+ local function ProcessVerticalInput(inputY, sliderFrame, minVal, maxVal, step)
294
+ local frameY = sliderFrame.AbsolutePosition.Y
295
+ local frameH = sliderFrame.AbsoluteSize.Y
296
+ if frameH <= 0 then return minVal end
297
+
298
+ local relativeY = math.clamp(inputY - frameY, 0, frameH)
299
+ local ratio = 1 - (relativeY / frameH) -- Dari bawah ke atas
300
+ local rawVal = minVal + ratio * (maxVal - minVal)
301
+ local steps = math.floor(rawVal / step + 0.5)
302
+ return math.clamp(steps * step, minVal, maxVal), ratio
303
+ end
304
+
305
+ local function UpdateVerticalVisual(button, ratio, textLabel, val)
306
+ button.Position = UDim2.new(
307
+ button.Position.X.Scale,
308
+ button.Position.X.Offset,
309
+ (1 - ratio),
310
+ -button.Size.Y.Offset / 2
311
+ )
312
+ if textLabel then
313
+ textLabel.Text = tostring(math.floor(val * 100) / 100)
314
+ end
315
+ end
316
+ ```
317
+
318
+ ---
319
+
320
+ ### B. Horizontal Slider (`HorizontalSlider`)
321
+ Diadopsi dari `NVMusic` (Volume Horizontal, Master Crossfade) dan `NVDance` (Emote Speed Slider):
322
+ - **Struktur Instance:**
323
+ - `SliderFrame` (Track Horizontal: Height `0.2` - `0.25`, Width `1.0`, background `#101010`, `UICorner`, `UIStroke`).
324
+ - `Fill` (Frame Bar kemajuan aksen Cyan/Putih, Size `UDim2.new(ratio, 0, 1, 0)`).
325
+ - `Button` / `Knob` (Frame/Button bulat, Position `UDim2.new(ratio, 0, 0.5, 0)`).
326
+ - `Trigger` (TextButton transparan pembungkus input sentuh `Size = UDim2.new(1, 0, 1, 0)`).
327
+ - **Logika Luau:**
328
+ ```lua
329
+ local function ProcessHorizontalInput(inputX, barFrame, minVal, maxVal, step)
330
+ local frameX = barFrame.AbsolutePosition.X
331
+ local frameW = barFrame.AbsoluteSize.X
332
+ if frameW <= 0 then return minVal end
333
+
334
+ local relX = math.clamp((inputX - frameX) / frameW, 0, 1)
335
+ local rawVal = minVal + relX * (maxVal - minVal)
336
+ local steps = math.floor(rawVal / step + 0.5)
337
+ return math.clamp(steps * step, minVal, maxVal), relX
338
+ end
339
+
340
+ local function UpdateHorizontalVisual(fillFrame, knobFrame, ratio, valueLabel, val)
341
+ if fillFrame then fillFrame.Size = UDim2.new(ratio, 0, 1, 0) end
342
+ if knobFrame then knobFrame.Position = UDim2.new(ratio, -knobFrame.Size.X.Offset / 2, 0.5, 0) end
343
+ if valueLabel then valueLabel.Text = string.format("%.2f", val) end
344
+ end
345
+ ```
346
+
347
+ ---
348
+
349
+ ### C. Switch Button / Toggle (`ToggleSwitch`)
350
+ Diadopsi dari `NVMusic.GuiController.ToggleSwitch` (DJ Switch Controller):
351
+ - **Struktur Instance:**
352
+ - `SwitchContainer` (Frame Pill / Kapsul: `UICorner` CornerRadius `UDim.new(1, 0)`, background `#141414`).
353
+ - `SwitchFrame` (Indicator bulat / knob kecil di dalam container, `UICorner` `UDim.new(1, 0)`).
354
+ - `SwitchButton` (TextButton transparan `Size = UDim2.new(1, 0, 1, 0)`, `BackgroundTransparency = 1`).
355
+ - **Logika & Animasi Tween Luau:**
356
+ ```lua
357
+ local ThemeColors = {
358
+ ActiveItem = Color3.fromRGB(0, 170, 255), -- Cyan saat ON
359
+ Background = Color3.fromRGB(20, 20, 20), -- Gelap saat OFF
360
+ White = Color3.fromRGB(255, 255, 255),
361
+ SubText = Color3.fromRGB(150, 150, 150)
362
+ }
363
+
364
+ local function SetToggleState(isOn: boolean, container: Frame, indicator: Frame, animate: boolean)
365
+ local bgColor = isOn and ThemeColors.ActiveItem or ThemeColors.Background
366
+ local offset = indicator and indicator.Size.X.Offset or 14
367
+ local indicatorPos = isOn and UDim2.new(1, -(offset + 2), 0.5, 0) or UDim2.new(0, 2, 0.5, 0)
368
+ local indicatorColor = isOn and ThemeColors.White or ThemeColors.SubText
369
+
370
+ if animate then
371
+ TweenService:Create(container, TweenInfo.new(0.2), { BackgroundColor3 = bgColor }):Play()
372
+ TweenService:Create(indicator, TweenInfo.new(0.2), { Position = indicatorPos, BackgroundColor3 = indicatorColor }):Play()
373
+ else
374
+ container.BackgroundColor3 = bgColor
375
+ indicator.Position = indicatorPos
376
+ indicator.BackgroundColor3 = indicatorColor
377
+ end
378
+ end
379
+ ```
380
+
381
+ ---
382
+
383
+ ### D. Segment Frame (Tab Bar Segment)
384
+ Diadopsi dari `NVMembership`, `NVServer`, `NVMusic`, dan `NVDance`:
385
+ - **Struktur Instance:**
386
+ - `SegmentFrame` (Container Tab Horizontal: `BackgroundColor3 = Color3.fromRGB(25, 25, 25)`, `UICorner` kapsul `UDim.new(1, 0)` atau `0.5`, `UIPadding`, `UIStroke`).
387
+ - `UIListLayout` (FillDirection `Horizontal`, HorizontalAlignment `Center`, SortOrder `LayoutOrder`).
388
+ - Tab Buttons (`ServerListButton`, `PlayerListButton`, `ManageButton`, dll) dengan `UICorner` rounded.
389
+ - **Logika Luau:**
390
+ ```lua
391
+ local function SetActiveTab(tabButtons: { [string]: TextButton }, activeTabName: string, contentFrames: { [string]: GuiObject })
392
+ for name, btn in pairs(tabButtons) do
393
+ if name == activeTabName then
394
+ btn.BackgroundColor3 = Color3.fromRGB(55, 55, 60) -- Active Tab
395
+ else
396
+ btn.BackgroundColor3 = Color3.fromRGB(25, 25, 25) -- Inactive Tab
397
+ end
398
+ end
399
+
400
+ for name, frame in pairs(contentFrames) do
401
+ frame.Visible = (name == activeTabName)
402
+ end
403
+ end
404
+ ```
405
+
406
+ ---
407
+
408
+ ### E. Standar Tombol Aktif & Inaktif (Button State Styling)
409
+ 1. **Gaya Tab / Segment (`ThemeColors`):**
410
+ - **Active:** `Color3.fromRGB(55, 55, 60)` (abu-abu terang kontras).
411
+ - **Inactive:** `Color3.fromRGB(25, 25, 25)` (abu-abu gelap menyatu dengan background).
412
+ 2. **Gaya Seleksi Item (`SelectStroke`):**
413
+ - Menggunakan child `UIStroke` bernama `SelectStroke` pada tombol:
414
+ - **Active (Selected):** `Color3.fromRGB(0, 255, 255)` (Cyan menyala).
415
+ - **Inactive:** `Color3.fromRGB(60, 60, 65)` (Outline abu-abu gelap).
416
+ 3. **Gaya Disable / Cooldown (`AutoButtonColor`):**
417
+ - Saat dalam cooldown / limit habis: `AutoButtonColor = false`, teks diubah menjadi `"WAIT (Xs)"` atau `"OUT OF QUOTA"`, dan warna teks/latar diredupkan.
418
+
419
+
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: nvstudio-mcp-guide
3
+ description: Panduan umum untuk berinteraksi dengan Roblox Studio menggunakan MCP nvstudio-mcp.
4
+ ---
5
+ # nvstudio-mcp-guide
6
+
7
+ Gunakan skill ini sebagai referensi umum saat bekerja dengan Roblox Studio via `nvstudio-mcp`.
8
+
9
+ ## Dasar Pemanggilan
10
+ 1. Jika target path tidak diketahui, gunakan perintah eksplorasi seperti `get_children` atau `search_instance` (khusus pencarian berdasarkan nama) terlebih dahulu.
11
+ 2. Tentukan target path string yang valid (misalnya `game.Workspace.Model.Part`). LLM/Agent tidak bisa berinteraksi menggunakan reference Luau langsung.
12
+ 3. Gunakan native semantic tools yang tersedia (`execute_luau`, `batch_create_instances`, `get_roblox_docs`, dll.) atau gunakan legacy dispatcher `call_mcp_tool` untuk kompatibilitas.
13
+
14
+ ## Batch Instance Builder (`batch_create_instances` & `batch_set_properties`)
15
+ Saat membuat banyak instance (terutama struktur GUI seperti `MainFrame`, `UICorner`, `UIStroke`, `UIListLayout`, `UIPadding`, `ScrollingFrame`, `Spacer`), **JANGAN** membuat instance satu per satu dengan `create_instance`. Gunakan `batch_create_instances`:
16
+ - **Mode `tree` (Rekomendasi Utama UI):**
17
+ Membangun seluruh hierarki bersarang dalam 1 roundtrip:
18
+ ```json
19
+ {
20
+ "target": "StarterGui.NVMenu",
21
+ "tree": {
22
+ "className": "Frame",
23
+ "name": "MainFrame",
24
+ "properties": {
25
+ "Size": [0.5, 0, 0.6, 0],
26
+ "Position": [0.5, 0, 0.5, 0],
27
+ "AnchorPoint": [0.5, 0.5],
28
+ "BackgroundColor3": [28, 28, 30]
29
+ },
30
+ "children": [
31
+ { "className": "UICorner", "name": "UICorner", "properties": { "CornerRadius": [0, 10] } },
32
+ { "className": "UIStroke", "name": "UIStroke", "properties": { "Color": [63, 63, 63], "Thickness": 1 } },
33
+ { "className": "UIListLayout", "name": "UIListLayout", "properties": { "SortOrder": "LayoutOrder" } },
34
+ { "className": "UIPadding", "name": "UIPadding", "properties": { "PaddingTop": [0, 10] } }
35
+ ]
36
+ }
37
+ }
38
+ ```
39
+ - **Mode `instances` (Flat Array):**
40
+ Membuat banyak objek di berbagai parent berbeda sekaligus:
41
+ ```json
42
+ {
43
+ "instances": [
44
+ { "className": "Part", "name": "LightA", "parent": "Workspace.Lights", "properties": { "Anchored": true } },
45
+ { "className": "Part", "name": "LightB", "parent": "Workspace.Lights", "properties": { "Anchored": true } }
46
+ ]
47
+ }
48
+ ```
49
+ - **Pembaruan Massal (`batch_set_properties`):**
50
+ Untuk mengubah properti banyak instance secara atomik dalam 1 pemanggilan:
51
+ ```json
52
+ {
53
+ "updates": [
54
+ { "target": "Workspace.PartA", "properties": { "Transparency": 0.5, "CanCollide": false } },
55
+ { "target": "Workspace.PartB", "properties": { "Color": [255, 0, 0] } }
56
+ ]
57
+ }
58
+ ```
59
+
60
+ ## Verifikasi Dokumentasi Engine Resmi (`get_roblox_docs`)
61
+ Sebelum menulis skrip Luau atau menggunakan API/Service yang belum pasti nama properti atau method-nya, **selalu panggil `get_roblox_docs`**:
62
+ - **Format Panggilan:**
63
+ - `query`: Nama class, service, datatype, enum, atau library (contoh: `"TweenService"`, `"CanvasGroup"`, `"UDim2"`, `"EasingStyle"`, `"task"`).
64
+ - `member` *(opsional)*: Untuk menghemat token, tentukan nama method atau properti spesifik (contoh: `query: "CanvasGroup", member: "GroupTransparency"`).
65
+ - **Keunggulan:** Tool ini membaca data resmi terverifikasi dari Creator Docs dengan caching lokal berkecepatan tinggi (~2 ms) untuk mencegah halusinasi kode Luau.
66
+
67
+ ## Modifikasi Skrip Cerdas (`patch_script_source`)
68
+ - Jika hanya ingin mengubah sebagian baris kode tanpa menimpa seluruh file skrip, gunakan `patch_script_source` dengan parameter `search` dan `replacement`.
69
+ - Untuk penggantian skrip utuh, gunakan `update_script_source` (otomatis dicek oleh AST Validator di Node.js untuk mencegah syntax error fatal).
70
+
71
+ ## Rigging, Animasi Karakter & Efek Visual (v2.4+)
72
+ nvstudio-mcp dilengkapi dengan rangkaian modular tripartit:
73
+ 1. **NV-Rig Engine** (lihat skill `nvstudio-rig-engine`):
74
+ - Gunakan `inspect_rig` untuk diagnosa kesehatan sendi & anchor status.
75
+ - Gunakan `auto_rig` untuk membuat rig R6, R15, atau menautkan senjata (`Weapon`).
76
+ - Gunakan `edit_rig_joint` untuk memindahkan pivot tanpa dislokasi part anak.
77
+ - Gunakan `weld_assembly` untuk menyatukan armor/prop/kendaraan.
78
+ 2. **NV-Animation Engine** (lihat skill `nvstudio-animation-vfx`):
79
+ - Gunakan `animate_character` untuk membuat animasi resmi `KeyframeSequence` di `AnimSaves` (Walk, Slash, Punch, Idle).
80
+ - Gunakan `solve_limb_ik` untuk analytical 2-link LimbIK ke koordinat 3D.
81
+ - Gunakan `scrub_timeline` untuk scrubbing playhead waktu deterministik.
82
+ 3. **NV-VFX Subsystem** (lihat skill `nvstudio-animation-vfx`):
83
+ - Gunakan `scan_vfx` untuk memindai efek visual dan `apply_vfx` untuk merekonstruksinya dari database persisten.
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: nvstudio-rig-engine
3
+ description: Panduan AI Agent untuk memeriksa, membuat, mengedit poros (C0/C1 tanpa dislokasi), auto-rigging R6/R15/senjata, dan mengelas assembly model di Roblox Studio menggunakan NV-Rig Engine.
4
+ ---
5
+
6
+ # NV-Rig Engine: Headless Modular Rigging Suite Guide
7
+
8
+ Panduan resmi untuk AI Agent dalam mengoperasikan sistem rigging Roblox secara headless menggunakan **NV-Rig Engine**. Modul ini melengkapi **NV-Animation Engine** dan **NV-VFX Subsystem** untuk menghasilkan alur kerja karakter yang lengkap dan anti-halusinasi.
9
+
10
+ ---
11
+
12
+ ## 1. Kapan Harus Menggunakan NV-Rig Engine
13
+
14
+ Gunakan tool-tool NV-Rig Engine saat:
15
+ - Karakter atau model impor belum memiliki `Motor6D` / sendi gerak.
16
+ - Titik tumpu (*pivot*) bahu, siku, lutut, atau sayap berada di posisi yang salah dan perlu digeser tanpa merusak susunan visual part.
17
+ - Bagian tubuh masih ter-anchor (`Anchored = true`) sehingga animasi tidak dapat menggerakkannya.
18
+ - Ingin menautkan senjata, perisai, atau pedang ke tangan karakter (`Weapon Grip`).
19
+ - Ingin mengelas (*weld*) seluruh komponen helm, armor, atau kendaraan menjadi satu kesatuan kaku.
20
+ - Ingin menyalin konfigurasi sendi sisi kiri ke sisi kanan secara simetris cermin (*X-axis mirror*).
21
+
22
+ ---
23
+
24
+ ## 2. Inspeksi Rig Mendalam (`inspect_rig`)
25
+
26
+ Selalu awali dengan memanggil `inspect_rig` sebelum melakukan rigging atau animasi untuk mendeteksi potensi masalah secara dini:
27
+
28
+ ```json
29
+ {
30
+ "target": "Workspace.CharacterModel"
31
+ }
32
+ ```
33
+
34
+ ### Yang Didiagnosis oleh `inspect_rig`:
35
+ 1. **Daftar Joint**: Semua `Motor6D` dan `Weld`, lengkap dengan nama `Part0`, `Part1`, $C_0$, $C_1$, dan koordinat poros dunia 3D.
36
+ 2. **Anchor Warning**: Mendeteksi part yang masih `Anchored = true`. Part anak yang ter-anchor tidak akan bergerak saat dianimasikan!
37
+ 3. **Circular Dependency**: Mendeteksi jika ada siklus sambungan tertutup yang dapat membekukan simulasi fisika Roblox.
38
+ 4. **Root Part**: Menemukan calon part induk utama (misal `HumanoidRootPart` atau `Torso`).
39
+
40
+ ---
41
+
42
+ ## 3. Auto-Rigging Cerdas 1-Klik (`auto_rig`)
43
+
44
+ Alih-alih membuat belasan sendi satu per satu, gunakan `auto_rig` untuk membangun pohon hierarki standar dalam sekejap:
45
+
46
+ ### A. Auto-Rig Avatar R15
47
+ Membangun 15 sambungan Motor6D lengkap (`Root`, `Waist`, `Neck`, bahu, siku, pergelangan tangan, panggul, lutut, pergelangan kaki):
48
+ ```json
49
+ {
50
+ "target": "Workspace.CustomR15Avatar",
51
+ "rig_type": "R15"
52
+ }
53
+ ```
54
+
55
+ ### B. Auto-Rig Avatar R6
56
+ Membangun 6 sambungan Motor6D klasik (`RootJoint`, `Neck`, `Left Shoulder`, `Right Shoulder`, `Left Hip`, `Right Hip`):
57
+ ```json
58
+ {
59
+ "target": "Workspace.CustomR6Avatar",
60
+ "rig_type": "R6"
61
+ }
62
+ ```
63
+
64
+ ### C. Auto-Rig Senjata / Tool
65
+ Menghubungkan senjata ke tangan karakter dengan `Motor6D` dinamai `WeaponGrip` dan mengelas seluruh bagian senjata lainnya ke `Handle`:
66
+ ```json
67
+ {
68
+ "target": "Workspace.Knight",
69
+ "rig_type": "Weapon",
70
+ "weapon_target": "Workspace.GreatSword",
71
+ "target_limb": "RightHand"
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 4. Manipulasi Poros Sendi Presisi (`edit_rig_joint`)
78
+
79
+ Gunakan `edit_rig_joint` untuk menggeser, memutar, mereset, menyelaraskan, atau membuat sendi baru.
80
+
81
+ ### A. Geser Poros Sendi (Non-Dislocation Law)
82
+ Menggeser titik engsel sendi di dunia nyata tanpa membuat part anak terlempar:
83
+ ```json
84
+ {
85
+ "target": "Workspace.CharacterModel",
86
+ "action": "edit_pivot",
87
+ "joint_name": "RightShoulder",
88
+ "delta_world": [0, 0.5, 0]
89
+ }
90
+ ```
91
+
92
+ ### B. Pindahkan Poros ke Koordinat Dunia Tertentu
93
+ Menempatkan titik engsel tepat pada koordinat target 3D:
94
+ ```json
95
+ {
96
+ "target": "Workspace.CharacterModel",
97
+ "action": "edit_pivot",
98
+ "joint_name": "RightElbow",
99
+ "target_world": [10.5, 5.0, -12.3]
100
+ }
101
+ ```
102
+
103
+ ### C. Putar Orientasi Poros
104
+ Memutar orientasi sumbu lokal sendi sebesar sudut Euler derajat:
105
+ ```json
106
+ {
107
+ "target": "Workspace.CharacterModel",
108
+ "action": "rotate_pivot",
109
+ "joint_name": "Neck",
110
+ "rotate_euler_deg": [0, 90, 0]
111
+ }
112
+ ```
113
+
114
+ ### D. Reset Poros ke Pusat Part Anak
115
+ Mengembalikan $C_0$ langsung ke origin Part1 dan mengosongkan $C_1$:
116
+ ```json
117
+ {
118
+ "target": "Workspace.CharacterModel",
119
+ "action": "reset_pivot",
120
+ "joint_name": "LeftShoulder"
121
+ }
122
+ ```
123
+
124
+ ### E. Buat Sendi Baru Secara Spesifik
125
+ ```json
126
+ {
127
+ "target": "Workspace.CharacterModel",
128
+ "action": "create_joint",
129
+ "joint_name": "TailJoint1",
130
+ "part0": "LowerTorso",
131
+ "part1": "TailBase"
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## 5. Pengelasan Assembly Model (`weld_assembly`)
138
+
139
+ Gunakan `weld_assembly` untuk menyatukan seluruh komponen pada sebuah model (aksesori, sayap dekoratif, helm, armor, kendaraan) ke part jangkar utama:
140
+ ```json
141
+ {
142
+ "target": "Workspace.ArmorSet",
143
+ "primary_part": "Chestplate"
144
+ }
145
+ ```
146
+ *Catatan: Semua part anak otomatis di-unanchor dan dilas ke `Chestplate` menggunakan transformasi $C_0$ yang presisi.*
147
+
148
+ ---
149
+
150
+ ## 6. Pencerminan Simetri Bilateral (`mirror_rig`)
151
+
152
+ Jika Anda telah mengonfigurasi poros sendi sisi kiri (`LeftShoulder`, `LeftElbow`, dll.), gunakan `mirror_rig` untuk mencerminkan nilai $C_0$ dan $C_1$ ke sisi kanan secara simetris sepanjang sumbu $X$:
153
+ ```json
154
+ {
155
+ "target": "Workspace.CharacterModel",
156
+ "source_side": "Left"
157
+ }
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 7. Alur Kerja Komprehensif: Rigging $\rightarrow$ Animasi $\rightarrow$ VFX
163
+
164
+ Ketika pengguna meminta animasi pertarungan lengkap:
165
+ 1. **Langkah 1**: Panggil `inspect_rig` pada karakter. Jika belum ada joint, jalankan `auto_rig` (mode `"R15"`).
166
+ 2. **Langkah 2**: Jika ada senjata, panggil `weld_assembly` pada senjata, lalu panggil `auto_rig` mode `"Weapon"`.
167
+ 3. **Langkah 3**: Buat animasi halus menggunakan `animate_character` (misal aksi `"melee_slash"` dengan `vfx_events`).
168
+ 4. **Langkah 4**: Pasang efek visual menggunakan `apply_vfx` dari database persisten.
169
+ 5. **Langkah 5**: Scrub timeline menggunakan `scrub_timeline` ke timestamp tebasan untuk memverifikasi pose dan partikel.
170
+
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: nvstudio-roblox-sync-guide
3
+ description: Aturan anti-halusinasi untuk membaca, mengedit, dan me-rollback script Roblox.
4
+ ---
5
+ # nvstudio-roblox-sync-guide
6
+
7
+ Panduan sinkronisasi dan manajemen skrip (properti `.Source`) di Roblox Studio.
8
+
9
+ ## Aturan update_script_source
10
+ 1. **PENTING:** Anda WAJIB melakukan `get_script_source` terlebih dahulu sebelum mengedit script apa pun. Ini agar Anda memahami kode apa yang ada saat ini dan mencegah hilangnya kode krusial.
11
+ 2. Operasi ini menimpa KESELURUHAN kode di dalam skrip.
12
+ 3. Server jembatan memiliki "Luau AST Validator" internal. Jika script Anda mengandung syntax error fatal (seperti lupa `end`), file tidak akan diupdate dan Anda akan menerima pesan error.
13
+
14
+ ## Aturan rollback_script
15
+ 1. Anda dapat membatalkan perubahan terakhir pada script dengan memanggil `rollback_script`.
16
+ 2. Pastikan target path cocok persis dengan script yang baru saja Anda edit.