feat(rbxl): XML-������ ������ .rbxl + Day/Night + Tool/Mouse/Backpack flow #38
504
RUBLOX_LUA_API.md
Normal file
504
RUBLOX_LUA_API.md
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
# Lua API Рублокса (справочник для скриптеров)
|
||||||
|
|
||||||
|
Этот документ — полный список того, что работает в Lua-скриптах Рублокса.
|
||||||
|
API максимально приближен к Roblox, чтобы можно было переносить чужие
|
||||||
|
скрипты с минимальными правками.
|
||||||
|
|
||||||
|
> **Как переключить скрипт на Lua:** в шапке вкладки редактора кода кликни
|
||||||
|
> по переключателю **JS / Lua**. Подсветка синтаксиса и автодополнение
|
||||||
|
> автоматически переключатся.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Содержание
|
||||||
|
|
||||||
|
1. [Базовые типы](#базовые-типы)
|
||||||
|
2. [DataModel: game, workspace, Players](#datamodel)
|
||||||
|
3. [Part — куб на сцене](#part)
|
||||||
|
4. [Создание и удаление](#создание-и-удаление)
|
||||||
|
5. [События: Touched, Heartbeat, RemoteEvent](#события)
|
||||||
|
6. [Таймеры: task.wait, task.delay](#таймеры)
|
||||||
|
7. [GUI: TextLabel, TextButton, Frame](#gui)
|
||||||
|
8. [Звук: Sound](#звук)
|
||||||
|
9. [Анимации: TweenService](#tweenservice)
|
||||||
|
10. [Игрок: Humanoid, LocalPlayer](#игрок)
|
||||||
|
11. [Чего пока нет](#чего-пока-нет)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Базовые типы
|
||||||
|
|
||||||
|
### `Vector3`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local v = Vector3.new(1, 2, 3)
|
||||||
|
print(v.X, v.Y, v.Z) -- 1 2 3
|
||||||
|
print(v.Magnitude) -- 3.7416... (длина)
|
||||||
|
print(v.Unit) -- нормализованный
|
||||||
|
print(v:Dot(otherVec)) -- скалярное произведение
|
||||||
|
print(v:Cross(otherVec)) -- векторное произведение
|
||||||
|
local mid = v:Lerp(otherVec, 0.5) -- линейная интерполяция
|
||||||
|
|
||||||
|
-- Константы:
|
||||||
|
Vector3.zero -- (0,0,0)
|
||||||
|
Vector3.one -- (1,1,1)
|
||||||
|
Vector3.xAxis -- (1,0,0)
|
||||||
|
Vector3.yAxis, Vector3.zAxis
|
||||||
|
```
|
||||||
|
|
||||||
|
Поддержаны операторы: `+`, `-`, `*` (на число), `/`, унарный `-`.
|
||||||
|
|
||||||
|
### `Color3`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local c = Color3.new(0.5, 0.2, 0.8) -- 0..1 каждый
|
||||||
|
local c2 = Color3.fromRGB(255, 128, 0) -- 0..255
|
||||||
|
local c3 = Color3.fromHSV(0.1, 0.8, 1)
|
||||||
|
local c4 = Color3.fromHex("#FF8000")
|
||||||
|
local mid = c:Lerp(c2, 0.5)
|
||||||
|
print(c:ToHex()) -- "#7F33CC"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `UDim2` / `UDim` / `Vector2`
|
||||||
|
|
||||||
|
Для GUI-координат:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pos = UDim2.new(0.5, 0, 0.5, 0) -- центр экрана (scale/offset)
|
||||||
|
local pos2 = UDim2.fromScale(0.2, 0.1)
|
||||||
|
local pos3 = UDim2.fromOffset(100, 50) -- в пикселях
|
||||||
|
```
|
||||||
|
|
||||||
|
### `CFrame`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local cf = CFrame.new(0, 10, 0) -- позиция
|
||||||
|
local cf2 = CFrame.lookAt(eye, target) -- упрощённый
|
||||||
|
print(cf.Position) -- Vector3
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Enum`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
Enum.KeyCode.W
|
||||||
|
Enum.KeyCode.Space
|
||||||
|
Enum.Material.Plastic, Enum.Material.Neon, Enum.Material.Wood
|
||||||
|
Enum.UserInputType.MouseButton1
|
||||||
|
Enum.HumanoidStateType.Running
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DataModel
|
||||||
|
|
||||||
|
Виртуальное дерево, как в Roblox:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
game -- корневой DataModel
|
||||||
|
game.Workspace -- = workspace (короче)
|
||||||
|
game.Players -- сервис игроков
|
||||||
|
game.Players.LocalPlayer -- локальный игрок
|
||||||
|
game.ReplicatedStorage -- хранилище общих ресурсов
|
||||||
|
game.StarterGui -- стартовое GUI
|
||||||
|
game.Lighting -- свет
|
||||||
|
```
|
||||||
|
|
||||||
|
Методы:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local svc = game:GetService("RunService")
|
||||||
|
local part = workspace:FindFirstChild("Coin")
|
||||||
|
local part2 = workspace:FindFirstChildOfClass("Part")
|
||||||
|
local all = workspace:GetChildren() -- массив всех детей
|
||||||
|
local descendants = workspace:GetDescendants()
|
||||||
|
local sib = workspace.Coin:FindFirstAncestorOfClass("Workspace")
|
||||||
|
print(workspace:IsA("Workspace")) -- true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Part
|
||||||
|
|
||||||
|
`Part` — куб/сфера/цилиндр на сцене. **Это обёртка над примитивом Рублокса.**
|
||||||
|
Скрипт привязанный к кубу получает его через `script.Parent`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- script.Parent — Part к которому прицеплен скрипт
|
||||||
|
print(script.Parent.Name) -- "Part_1"
|
||||||
|
|
||||||
|
-- Чтение свойств
|
||||||
|
print(script.Parent.Position) -- Vector3
|
||||||
|
print(script.Parent.Size) -- Vector3
|
||||||
|
print(script.Parent.Color) -- Color3
|
||||||
|
print(script.Parent.Anchored) -- bool
|
||||||
|
print(script.Parent.CanCollide) -- bool
|
||||||
|
print(script.Parent.Transparency) -- 0..1
|
||||||
|
|
||||||
|
-- Запись (двигает куб в реальном времени!)
|
||||||
|
script.Parent.Position = Vector3.new(0, 10, 0)
|
||||||
|
script.Parent.Size = Vector3.new(5, 1, 5)
|
||||||
|
script.Parent.Color = Color3.fromRGB(255, 0, 0)
|
||||||
|
script.Parent.Anchored = false -- куб начнёт падать (физика)
|
||||||
|
script.Parent.Transparency = 0.5 -- полупрозрачный
|
||||||
|
script.Parent.CFrame = CFrame.new(0, 20, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Создание и удаление
|
||||||
|
|
||||||
|
### `Instance.new`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Создать Part на сцене
|
||||||
|
local p = Instance.new("Part")
|
||||||
|
p.Position = Vector3.new(0, 5, 0)
|
||||||
|
p.Size = Vector3.new(2, 2, 2)
|
||||||
|
p.Color = Color3.fromRGB(255, 100, 0)
|
||||||
|
p.Anchored = true
|
||||||
|
p.Parent = workspace
|
||||||
|
|
||||||
|
-- Удалить через 3 секунды
|
||||||
|
task.delay(3, function()
|
||||||
|
p:Destroy()
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
Поддержанные классы:
|
||||||
|
- **Сцена:** `Part`, `WedgePart`, `MeshPart`
|
||||||
|
- **События:** `RemoteEvent`, `BindableEvent`
|
||||||
|
- **GUI:** `ScreenGui`, `Frame`, `TextLabel`, `TextButton`, `ImageLabel`,
|
||||||
|
`ImageButton`, `TextBox`, `ScrollingFrame`
|
||||||
|
- **Звук:** `Sound`
|
||||||
|
- **Прочее:** `Folder`, `Humanoid`, `Configuration`, любой `ClassName`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## События
|
||||||
|
|
||||||
|
### `script.Parent.Touched` — касание игрока
|
||||||
|
|
||||||
|
```lua
|
||||||
|
script.Parent.Touched:Connect(function(hit)
|
||||||
|
print("Игрок коснулся!", hit.Name)
|
||||||
|
local h = game.Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
|
||||||
|
if h then
|
||||||
|
h:TakeDamage(100) -- KillBrick
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `RunService.Heartbeat` — каждый кадр
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
RunService.Heartbeat:Connect(function(dt)
|
||||||
|
-- dt — время с прошлого кадра (~0.016)
|
||||||
|
script.Parent.Position = script.Parent.Position + Vector3.new(0, 0.1, 0)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `BindableEvent` / `RemoteEvent` — общение между скриптами
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Скрипт A создаёт событие в общем месте
|
||||||
|
local event = Instance.new("BindableEvent")
|
||||||
|
event.Name = "MyEvent"
|
||||||
|
event.Parent = game.ReplicatedStorage
|
||||||
|
|
||||||
|
-- Скрипт B подписывается
|
||||||
|
local event = game.ReplicatedStorage:WaitForChild("MyEvent")
|
||||||
|
event.Event:Connect(function(msg, num)
|
||||||
|
print("Получено:", msg, num)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Скрипт A триггерит
|
||||||
|
event:Fire("привет", 42)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Humanoid.Died`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local h = game.Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
|
||||||
|
h.Died:Connect(function()
|
||||||
|
print("игрок умер")
|
||||||
|
end)
|
||||||
|
h.HealthChanged:Connect(function(newHp)
|
||||||
|
print("здоровье:", newHp)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Таймеры
|
||||||
|
|
||||||
|
### `task.wait(сек)` — приостановить скрипт
|
||||||
|
|
||||||
|
```lua
|
||||||
|
print("сейчас")
|
||||||
|
task.wait(1)
|
||||||
|
print("через секунду")
|
||||||
|
```
|
||||||
|
|
||||||
|
`task.wait` **не блокирует** другие скрипты — это yield через coroutines.
|
||||||
|
Можно использовать в `while true do ... task.wait(0.1) end` без проблем.
|
||||||
|
|
||||||
|
### `task.delay(сек, fn)` — выполнить через
|
||||||
|
|
||||||
|
```lua
|
||||||
|
task.delay(2, function()
|
||||||
|
print("через 2 секунды")
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `task.spawn(fn)` — асинхронно
|
||||||
|
|
||||||
|
```lua
|
||||||
|
task.spawn(function()
|
||||||
|
print("параллельно с основным потоком")
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## GUI
|
||||||
|
|
||||||
|
### Базовая иерархия
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- ScreenGui — корень всех GUI
|
||||||
|
local sg = Instance.new("ScreenGui")
|
||||||
|
sg.Parent = game.Players.LocalPlayer.PlayerGui
|
||||||
|
|
||||||
|
-- TextLabel — статичный текст
|
||||||
|
local label = Instance.new("TextLabel")
|
||||||
|
label.Parent = sg
|
||||||
|
label.Text = "Привет!"
|
||||||
|
label.TextColor3 = Color3.fromRGB(255, 255, 0)
|
||||||
|
label.BackgroundColor3 = Color3.fromRGB(50, 30, 20)
|
||||||
|
label.Position = UDim2.new(0.4, 0, 0.1, 0) -- 40% от ширины, 10% от высоты
|
||||||
|
label.Size = UDim2.new(0.2, 0, 0.05, 0)
|
||||||
|
label.TextSize = 24
|
||||||
|
|
||||||
|
-- TextButton — кликабельная кнопка
|
||||||
|
local btn = Instance.new("TextButton")
|
||||||
|
btn.Parent = sg
|
||||||
|
btn.Text = "Нажми"
|
||||||
|
btn.Position = UDim2.new(0.4, 0, 0.5, 0)
|
||||||
|
btn.Size = UDim2.new(0.2, 0, 0.08, 0)
|
||||||
|
btn.MouseButton1Click:Connect(function()
|
||||||
|
print("Клик!")
|
||||||
|
label.Text = "Нажата!"
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Свойства
|
||||||
|
|
||||||
|
| Свойство | Тип | Описание |
|
||||||
|
|----------------------|-----------|-----------------------------------|
|
||||||
|
| `Text` | string | Видимый текст |
|
||||||
|
| `TextColor3` | Color3 | Цвет текста |
|
||||||
|
| `TextSize` | number | Размер шрифта |
|
||||||
|
| `BackgroundColor3` | Color3 | Цвет фона |
|
||||||
|
| `BackgroundTransparency` | 0..1 | 0=сплошной, 1=прозрачный |
|
||||||
|
| `Position` | UDim2 | Позиция (scale=%, offset=px/10) |
|
||||||
|
| `Size` | UDim2 | Размер |
|
||||||
|
| `Visible` | bool | Виден или нет |
|
||||||
|
|
||||||
|
### События кнопок
|
||||||
|
|
||||||
|
```lua
|
||||||
|
btn.MouseButton1Click:Connect(fn) -- ЛКМ клик
|
||||||
|
btn.MouseEnter:Connect(fn) -- наведение
|
||||||
|
btn.MouseLeave:Connect(fn) -- увод
|
||||||
|
btn.Activated:Connect(fn) -- = MouseButton1Click
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Звук
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local sound = Instance.new("Sound")
|
||||||
|
sound.SoundId = "coin" -- или "jump", "win", "lose", "hit", "click", "pickup"
|
||||||
|
sound.Volume = 1 -- 0..2
|
||||||
|
sound.PlaybackSpeed = 1 -- pitch
|
||||||
|
sound:Play()
|
||||||
|
```
|
||||||
|
|
||||||
|
Также Roblox-AssetID работает с эвристикой:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
sound.SoundId = "rbxassetid://1234567890" -- автоподбор по имени переменной
|
||||||
|
```
|
||||||
|
|
||||||
|
Поддержанные звуки (процедурные, не из файлов):
|
||||||
|
- `jump` — прыжок
|
||||||
|
- `pickup` — подбор
|
||||||
|
- `coin` — звон монеты
|
||||||
|
- `win` — победа
|
||||||
|
- `lose` — поражение
|
||||||
|
- `click` — клик
|
||||||
|
- `hit` — удар
|
||||||
|
|
||||||
|
Зацикливание:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
sound.Looped = true
|
||||||
|
sound:Play() -- играет до sound:Stop()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TweenService
|
||||||
|
|
||||||
|
Плавная анимация свойств:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local TweenService = game:GetService("TweenService")
|
||||||
|
|
||||||
|
local part = script.Parent
|
||||||
|
local tween = TweenService:Create(
|
||||||
|
part,
|
||||||
|
{ Time = 2 }, -- длительность 2 сек
|
||||||
|
{ Position = Vector3.new(0, 20, 0),
|
||||||
|
Color = Color3.fromRGB(255, 0, 0) } -- цели
|
||||||
|
)
|
||||||
|
tween:Play()
|
||||||
|
|
||||||
|
tween.Completed:Connect(function()
|
||||||
|
print("Анимация завершилась!")
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
Работает с `Position`, `Size`, `Color` (Vector3/Color3) и числовыми
|
||||||
|
свойствами (`Transparency`, `TextSize`, и т.д.).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Игрок
|
||||||
|
|
||||||
|
### `game.Players.LocalPlayer`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local plr = game.Players.LocalPlayer
|
||||||
|
print(plr.Name, plr.UserId, plr.DisplayName)
|
||||||
|
print(plr.Character) -- Model
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Humanoid`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local char = game.Players.LocalPlayer.Character
|
||||||
|
local h = char:FindFirstChildOfClass("Humanoid")
|
||||||
|
|
||||||
|
print(h.Health, h.MaxHealth)
|
||||||
|
print(h.WalkSpeed) -- скорость ходьбы
|
||||||
|
print(h.JumpPower) -- сила прыжка
|
||||||
|
|
||||||
|
h.Health = 0 -- мгновенная смерть → респавн
|
||||||
|
h:TakeDamage(50) -- урон с учётом invulnerability
|
||||||
|
|
||||||
|
h.Died:Connect(function()
|
||||||
|
print("Помер")
|
||||||
|
end)
|
||||||
|
h.HealthChanged:Connect(function(newHp)
|
||||||
|
if newHp < 30 then
|
||||||
|
print("Здоровье низкое!")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `HumanoidRootPart`
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local hrp = char:FindFirstChild("HumanoidRootPart")
|
||||||
|
print(hrp.Position)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Чего пока нет
|
||||||
|
|
||||||
|
Не работает (пока):
|
||||||
|
|
||||||
|
- **Скрипты не делятся на Server/LocalScript** — все скрипты client-side.
|
||||||
|
- **DataStoreService** — методы есть, но возвращают nil/no-op.
|
||||||
|
- **`workspace:Raycast`** / **`game.Lighting.ClockTime`** — заглушки.
|
||||||
|
- **`Players.PlayerAdded`** — никогда не фейерится (только один игрок).
|
||||||
|
- **3D-анимации (`Animation` instance + `AnimationController`)** —
|
||||||
|
`LoadAnimation` возвращает заглушку.
|
||||||
|
- **`Sound` из файлов** — только встроенные процедурные.
|
||||||
|
- **`SurfaceGui` / `BillboardGui`** — нет, только `ScreenGui`.
|
||||||
|
- **`Model:MoveTo` / `:SetPrimaryPartCFrame`** — нет.
|
||||||
|
- **Networking (`RemoteFunction:InvokeServer`)** — RemoteEvent работает
|
||||||
|
только в пределах одного клиента.
|
||||||
|
|
||||||
|
Если что-то из этого критично — открой issue в репо.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример: KillBrick + монета + GUI-счётчик
|
||||||
|
|
||||||
|
Положи 1 куб и 1 шарик на сцене. К каждому привяжи скрипт:
|
||||||
|
|
||||||
|
**На кубе (KillBrick):**
|
||||||
|
```lua
|
||||||
|
script.Parent.Color = Color3.fromRGB(200, 30, 30)
|
||||||
|
script.Parent.Touched:Connect(function()
|
||||||
|
local h = game.Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
|
||||||
|
if h then h:TakeDamage(100) end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
**На шарике (Coin):**
|
||||||
|
```lua
|
||||||
|
script.Parent.Color = Color3.fromRGB(255, 215, 0)
|
||||||
|
script.Parent.Touched:Connect(function()
|
||||||
|
-- Запускаем событие на ReplicatedStorage
|
||||||
|
local re = game.ReplicatedStorage:FindFirstChild("CoinPicked")
|
||||||
|
if not re then
|
||||||
|
re = Instance.new("BindableEvent")
|
||||||
|
re.Name = "CoinPicked"
|
||||||
|
re.Parent = game.ReplicatedStorage
|
||||||
|
end
|
||||||
|
re:Fire()
|
||||||
|
script.Parent:Destroy()
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Глобальный скрипт (GUI):**
|
||||||
|
```lua
|
||||||
|
local sg = Instance.new("ScreenGui")
|
||||||
|
sg.Parent = game.Players.LocalPlayer.PlayerGui
|
||||||
|
|
||||||
|
local label = Instance.new("TextLabel")
|
||||||
|
label.Parent = sg
|
||||||
|
label.Text = "Монет: 0"
|
||||||
|
label.Position = UDim2.new(0.05, 0, 0.05, 0)
|
||||||
|
label.Size = UDim2.new(0.1, 0, 0.05, 0)
|
||||||
|
label.TextSize = 20
|
||||||
|
label.TextColor3 = Color3.fromRGB(255, 215, 0)
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
task.spawn(function()
|
||||||
|
while not game.ReplicatedStorage:FindFirstChild("CoinPicked") do
|
||||||
|
task.wait(0.1)
|
||||||
|
end
|
||||||
|
game.ReplicatedStorage.CoinPicked.Event:Connect(function()
|
||||||
|
count = count + 1
|
||||||
|
label.Text = "Монет: " .. count
|
||||||
|
local sound = Instance.new("Sound")
|
||||||
|
sound.SoundId = "coin"
|
||||||
|
sound:Play()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
Получится: красный куб убивает, золотая монета даёт +1 к счётчику со
|
||||||
|
звуком.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Версия документации:** Этап 7 (готово после реализации Этапов 1-6).
|
||||||
|
Если что-то описанное здесь не работает — это баг, репортуй.
|
||||||
@ -610,7 +610,10 @@ export function registerRobloxShim(lua, opts) {
|
|||||||
|
|
||||||
makeService('Lighting').Ambient = new RbxColor3(0.5, 0.5, 0.5);
|
makeService('Lighting').Ambient = new RbxColor3(0.5, 0.5, 0.5);
|
||||||
makeService('Chat');
|
makeService('Chat');
|
||||||
makeService('SoundService');
|
const soundService = makeService('SoundService');
|
||||||
|
soundService.PlayLocalSound = function (sound) {
|
||||||
|
if (sound && typeof sound.Play === 'function') sound.Play();
|
||||||
|
};
|
||||||
makeService('PathfindingService');
|
makeService('PathfindingService');
|
||||||
makeService('CollectionService');
|
makeService('CollectionService');
|
||||||
makeService('MarketplaceService');
|
makeService('MarketplaceService');
|
||||||
@ -829,6 +832,53 @@ export function registerRobloxShim(lua, opts) {
|
|||||||
inst.Health = 100; inst.MaxHealth = 100;
|
inst.Health = 100; inst.MaxHealth = 100;
|
||||||
inst.Died = makeSignal(); inst.HealthChanged = makeSignal();
|
inst.Died = makeSignal(); inst.HealthChanged = makeSignal();
|
||||||
inst.TakeDamage = function (n) { this.Health = Math.max(0, this.Health - (n || 0)); };
|
inst.TakeDamage = function (n) { this.Health = Math.max(0, this.Health - (n || 0)); };
|
||||||
|
} else if (className === 'Sound') {
|
||||||
|
// Sound — процедурные звуки через _playSound.
|
||||||
|
// SoundId → имя процедурного звука (rbxassetid игнорится).
|
||||||
|
inst = newInstance('Sound', 'Sound');
|
||||||
|
inst.SoundId = '';
|
||||||
|
inst.Volume = 1;
|
||||||
|
inst.PlaybackSpeed = 1;
|
||||||
|
inst.Pitch = 1;
|
||||||
|
inst.Looped = false;
|
||||||
|
inst.IsPlaying = false;
|
||||||
|
inst.Played = makeSignal();
|
||||||
|
inst.Ended = makeSignal();
|
||||||
|
// Map SoundId/имя на встроенный звук (jump/pickup/win/lose/click/hit/coin).
|
||||||
|
const _mapSoundName = (idOrName) => {
|
||||||
|
if (!idOrName) return 'click';
|
||||||
|
const s = String(idOrName).toLowerCase();
|
||||||
|
// Прямые ключи имеют приоритет
|
||||||
|
if (['jump','pickup','win','lose','click','hit','coin'].indexOf(s) >= 0) return s;
|
||||||
|
// Эвристика по части строки (для Roblox AssetID)
|
||||||
|
if (s.includes('jump')) return 'jump';
|
||||||
|
if (s.includes('pickup') || s.includes('collect')) return 'pickup';
|
||||||
|
if (s.includes('win') || s.includes('victory')) return 'win';
|
||||||
|
if (s.includes('lose') || s.includes('death')) return 'lose';
|
||||||
|
if (s.includes('hit') || s.includes('damage')) return 'hit';
|
||||||
|
if (s.includes('coin') || s.includes('gem')) return 'coin';
|
||||||
|
return 'click';
|
||||||
|
};
|
||||||
|
inst.Play = function () {
|
||||||
|
const name = _mapSoundName(this.SoundId || this.Name);
|
||||||
|
const pitch = +this.PlaybackSpeed || +this.Pitch || 1;
|
||||||
|
const volume = +this.Volume || 1;
|
||||||
|
send('sound.play', { name, volume, pitch });
|
||||||
|
this.IsPlaying = true;
|
||||||
|
this.Played.Fire();
|
||||||
|
// Простая модель: считаем что звук длится 0.5с
|
||||||
|
SCHEDULER.sleeping.push({
|
||||||
|
wakeAt: SCHEDULER.now() + 500,
|
||||||
|
run: () => {
|
||||||
|
this.IsPlaying = false;
|
||||||
|
this.Ended.Fire();
|
||||||
|
if (this.Looped) this.Play();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
inst.Stop = function () { this.IsPlaying = false; };
|
||||||
|
inst.Pause = function () { this.IsPlaying = false; };
|
||||||
|
inst.Resume = function () { if (!this.IsPlaying) this.Play(); };
|
||||||
} else if (className === 'ScreenGui') {
|
} else if (className === 'ScreenGui') {
|
||||||
// ScreenGui — логический корень GUI. В Rublox overlay глобальный,
|
// ScreenGui — логический корень GUI. В Rublox overlay глобальный,
|
||||||
// поэтому ScreenGui это просто контейнер-no-op (без gui.create).
|
// поэтому ScreenGui это просто контейнер-no-op (без gui.create).
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user