/**
 * docsGamesBuildersLua.js — Lua-эквиваленты скриптов для уроков.
 *
 * Структура: LUA_OVERRIDES[gameId][scriptId] = 'lua-code'
 * Может быть строкой или функцией (script) => 'lua-code' для случаев,
 * когда код зависит от target/name (например, имя примитива).
 *
 * Когда юзер нажимает «Открыть копию → Lua» в LessonPage,
 * buildGameProject(id, {lang:'lua'}) подменяет JS-скрипт на Lua-версию
 * отсюда. Геометрия (примитивы, блоки) остаётся той же — отличается
 * только язык скриптов.
 *
 * Lua-код пишется в стандартном Roblox-стиле:
 *   game:GetService("Players"), workspace, Instance.new, Vector3, CFrame,
 *   :Connect, RunService.Heartbeat, BindableEvent через ReplicatedStorage.
 *
 * Конвенции:
 * - target=null (глобальный JS-скрипт) → в Lua это просто script в workspace,
 *   общение через ReplicatedStorage:BindableEvent.
 * - target.kind='primitive' (на объекте) → script лежит ВНУТРИ части,
 *   обращение к ней через script.Parent. Имя части совпадает с тем что
 *   в JS-builder указано (Монетка_N, Платформа_N и т.д.).
 *
 * Помощники общего назначения:
 *   getPlayerFromHit — извлекает Player из hit события Touched.
 *   getOrCreateEvent — общая BindableEvent в ReplicatedStorage для broadcast.
 */

// ══════════════════════════════════════════════════════════════════
// Общие сниппеты — вставляются в начало многих скриптов.
// ══════════════════════════════════════════════════════════════════
const SNIPPET_BROADCAST = `local ReplicatedStorage = game:GetService("ReplicatedStorage")
local function getEvent(name)
    local ev = ReplicatedStorage:FindFirstChild(name)
    if not ev then
        ev = Instance.new("BindableEvent")
        ev.Name = name
        ev.Parent = ReplicatedStorage
    end
    return ev
end`;

const SNIPPET_PLAYER_HIT = `local Players = game:GetService("Players")
local function getPlayerFromHit(hit)
    if not hit or not hit.Parent then return nil end
    return Players:GetPlayerFromCharacter(hit.Parent)
end`;

export const LUA_OVERRIDES = {

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 1 — «Собери монетки»
    // ═══════════════════════════════════════════════════════════════
    'collect-coins': {
        g1_main: `-- === ИГРА «СОБЕРИ МОНЕТКИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local score = 0
local TOTAL = 8

-- HUD: счётчик в правом верхнем углу
local player = Players.LocalPlayer
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
screenGui.Name = "CoinHUD"

local label = Instance.new("TextLabel", screenGui)
label.Size = UDim2.new(0, 220, 0, 50)
label.Position = UDim2.new(1, -240, 0, 20)
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
label.BackgroundTransparency = 0.4
label.TextColor3 = Color3.fromRGB(255, 215, 0)
label.TextScaled = true
label.Font = Enum.Font.SourceSansBold
label.Text = "Монеты: 0 / " .. TOTAL

-- Подсказка по центру (на 2 секунды)
local hintGui = Instance.new("ScreenGui", player.PlayerGui)
local hint = Instance.new("TextLabel", hintGui)
hint.Size = UDim2.new(0, 400, 0, 60)
hint.Position = UDim2.new(0.5, -200, 0.3, 0)
hint.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
hint.BackgroundTransparency = 0.4
hint.TextColor3 = Color3.fromRGB(255, 255, 255)
hint.TextScaled = true
hint.Text = "Собери все монетки!"
task.delay(2, function() hintGui:Destroy() end)

-- Звуки
local coinSound = Instance.new("Sound", workspace)
coinSound.SoundId = "coin"
coinSound.Volume = 1

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"
winSound.Volume = 1

-- Подписка на сбор монетки
local coinEvent = getEvent("CoinCollected")
coinEvent.Event:Connect(function()
    score = score + 1
    label.Text = "Монеты: " .. score .. " / " .. TOTAL
    coinSound:Play()
    if score >= TOTAL then
        -- Победный текст
        local winGui = Instance.new("ScreenGui", player.PlayerGui)
        local winLabel = Instance.new("TextLabel", winGui)
        winLabel.Size = UDim2.new(0, 500, 0, 80)
        winLabel.Position = UDim2.new(0.5, -250, 0.4, 0)
        winLabel.BackgroundColor3 = Color3.fromRGB(0, 100, 0)
        winLabel.BackgroundTransparency = 0.2
        winLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
        winLabel.TextScaled = true
        winLabel.Font = Enum.Font.SourceSansBold
        winLabel.Text = "Победа! Все монетки твои!"
        winSound:Play()
    end
end)`,
        // Скрипт каждой монетки — генератор по script-объекту
        g1_coin_1: makeCoinScript(),
        g1_coin_2: makeCoinScript(),
        g1_coin_3: makeCoinScript(),
        g1_coin_4: makeCoinScript(),
        g1_coin_5: makeCoinScript(),
        g1_coin_6: makeCoinScript(),
        g1_coin_7: makeCoinScript(),
        g1_coin_8: makeCoinScript(),
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 2 — «Прыгай по платформам»
    // ═══════════════════════════════════════════════════════════════
    'platform-jump': {
        g2_main: `-- === ИГРА «ПРЫГАЙ ПО ПЛАТФОРМАМ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

-- Подсказка по центру (паритет с JS game.ui.showText)
__rbxl_show_text("Допрыгай до зелёной площадки!", 3)

-- Звуки
local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"
loseSound.Volume = 1
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"
winSound.Volume = 1

-- Каждый кадр следим: не упал ли игрок
RunService.Heartbeat:Connect(function()
    if won then return end
    local char = player.Character
    if not char then return end
    local hrp = char:FindFirstChild("HumanoidRootPart")
    if hrp and hrp.Position.Y < -3 then
        player:LoadCharacter()
        loseSound:Play()
        __rbxl_show_text("Упал! Пробуй снова.", 1.5)
    end
end)

-- Финиш-зона шлёт BindableEvent
local finishEvent = getEvent("FinishReached")
finishEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты дошёл до финиша!", 5)
    -- Конфетти над игроком (паритет с JS game.scene.spawnParticles)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g2_finish: `-- === Скрипт финиш-зоны (Lua) ===
-- Висит на невидимой зоне над зелёной площадкой.
-- Игрок встал — его тело внутри зоны — победа.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("FinishReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 3 — «Не упади» (платформа сужается)
    // ═══════════════════════════════════════════════════════════════
    'dont-fall': (function() {
        const overrides = {
            g3_main: `-- === ИГРА «НЕ УПАДИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Беги вперёд! Плитки исчезают!", 3)

local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 1
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

RunService.Heartbeat:Connect(function()
    if won then return end
    local char = player.Character
    if not char then return end
    local hrp = char:FindFirstChild("HumanoidRootPart")
    if hrp and hrp.Position.Y < -3 then
        player:LoadCharacter()
        loseSound:Play()
        __rbxl_show_text("Упал! Снова.", 1.5)
    end
end)

local finishEvent = getEvent("FinishReached")
finishEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты добежал!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
            g3_finish: `-- === Скрипт финиш-зоны (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("FinishReached")
    if ev then ev:Fire() end
end)`,
        };
        // Скрипт каждой плитки — генератор (одинаковый код)
        const tileScript = `-- === Скрипт исчезающей плитки (Lua) ===
local Debris = game:GetService("Debris")
local part = script.Parent
local triggered = false
local clickSound = Instance.new("Sound", part)
clickSound.SoundId = "click"; clickSound.Volume = 0.6

part.Touched:Connect(function(hit)
    if triggered then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    triggered = true
    clickSound:Play()
    -- через 1.2с плитка пропадает
    Debris:AddItem(part, 1.2)
end)`;
        for (let i = 1; i <= 14; i++) overrides['g3_tile_' + i] = tileScript;
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 4 — «Кнопка и дверь»
    // ═══════════════════════════════════════════════════════════════
    'button-door': {
        g4_main: `-- === ИГРА «КНОПКА-ОТКРЫВАШКА» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

__rbxl_show_text("Подойди к красной кнопке и нажми E", 4)

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    __rbxl_show_text("Победа! Дверь открыта, ты прошёл!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g4_button: `-- === Скрипт кнопки (Lua) ===
-- Висит на красной кнопке. Реагирует на E когда игрок рядом.
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local part = script.Parent
local opened = false
local hintVisible = false

-- Подсказка над кнопкой. BillboardGui в shim — generic instance,
-- управляем видимостью через label.Visible.
local hintGui = Instance.new("BillboardGui", part)
hintGui.Size = UDim2.new(4, 0, 1, 0)
hintGui.StudsOffset = Vector3.new(0, 2, 0)
hintGui.AlwaysOnTop = true
local label = Instance.new("TextLabel", hintGui)
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.TextColor3 = Color3.fromRGB(255, 255, 255)
label.TextStrokeTransparency = 0
label.TextScaled = true
label.Text = "[E] Открыть дверь"
label.Visible = false  -- скрыт по умолчанию

local clickSound = Instance.new("Sound", part)
clickSound.SoundId = "click"; clickSound.Volume = 0.8

-- Каждый кадр проверяем расстояние до игрока. Подсказку показываем
-- только если игрок в радиусе 4 единиц.
RunService.Heartbeat:Connect(function()
    if opened then return end
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local dx = part.Position.X - px
    local dz = part.Position.Z - pz
    local dist = math.sqrt(dx*dx + dz*dz)
    local near = dist <= 4
    if near ~= hintVisible then
        hintVisible = near
        label.Visible = near
    end
end)

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if opened or not hintVisible then return end
    if input.KeyCode ~= Enum.KeyCode.E then return end

    opened = true
    label.Visible = false  -- скрываем подсказку (Destroy не уничтожает GUI-overlay)
    label:Destroy()
    hintGui:Destroy()
    clickSound:Play()
    __rbxl_show_text("Дверь открывается!", 2)
    part.Color = Color3.fromRGB(100, 255, 100)

    local door = workspace:FindFirstChild("Дверь")
    if door then
        local dp = door.Position
        local goal = { Position = Vector3.new(dp.X, dp.Y + 6, dp.Z) }
        TweenService:Create(door, TweenInfo.new(1.2), goal):Play()
        door.CanCollide = false
    end
end)`,
        g4_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 5 — «Лабиринт»
    // ═══════════════════════════════════════════════════════════════
    'maze': {
        g5_main: `-- === ИГРА «ЛАБИРИНТ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

__rbxl_show_text("Найди выход из лабиринта!", 3)

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    __rbxl_show_text("Победа! Ты нашёл выход!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g5_finish: `-- === Скрипт финиша лабиринта (Lua) ===
-- Висит на невидимой зоне над зелёным ковриком.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 6 — «Угадай цвет»
    // ═══════════════════════════════════════════════════════════════
    'color-tiles': (function() {
        const overrides = {
            g6_main: `-- === ИГРА «ЦВЕТНЫЕ ПЛИТКИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local painted = 0
local TOTAL = 36
local won = false

__rbxl_show_text("Наступи на все плитки!", 3)

-- Счётчик в правом верхнем углу
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
local label = Instance.new("TextLabel", screenGui)
label.Size = UDim2.new(0, 220, 0, 50)
label.Position = UDim2.new(1, -240, 0, 20)
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
label.BackgroundTransparency = 0.4
label.TextColor3 = Color3.fromRGB(160, 255, 160)
label.TextScaled = true
label.Font = Enum.Font.SourceSansBold
label.Text = "Плитки: 0 / " .. TOTAL

local pickupSound = Instance.new("Sound", workspace)
pickupSound.SoundId = "pickup"; pickupSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

local paintEvent = getEvent("TilePainted")
paintEvent.Event:Connect(function()
    if won then return end
    painted = painted + 1
    label.Text = "Плитки: " .. painted .. " / " .. TOTAL
    pickupSound:Play()
    if painted >= TOTAL then
        won = true
        winSound:Play()
        __rbxl_show_text("Победа! Все плитки раскрашены!", 5)
        local px = __rbxl_player_x()
        local py = __rbxl_player_y()
        local pz = __rbxl_player_z()
        __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
    end
end)`,
        };
        // Скрипт для каждой из 36 плиток — одинаковый код через генератор
        const tileScript = `-- === Скрипт цветной плитки (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local painted = false

part.Touched:Connect(function(hit)
    if painted then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    painted = true
    part.Color = Color3.fromRGB(51, 221, 85)  -- ярко-зелёный
    local ev = ReplicatedStorage:FindFirstChild("TilePainted")
    if ev then ev:Fire() end
end)`;
        for (let i = 1; i <= 36; i++) overrides['g6_tile_' + i] = tileScript;
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 7 — «Ловишка предметов»
    // ═══════════════════════════════════════════════════════════════
    'catch-falling': {
        g7_main: `-- === ИГРА «ПОЙМАЙ ПАДАЮЩЕЕ» — главный скрипт (Lua) ===
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")

local player = Players.LocalPlayer
local score = 0
local GOAL = 15
local won = false

__rbxl_show_text("Лови падающие кубы! Нужно 15", 3)

-- Счётчик в правом верхнем углу
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
local label = Instance.new("TextLabel", screenGui)
label.Size = UDim2.new(0, 220, 0, 50)
label.Position = UDim2.new(1, -240, 0, 20)
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
label.BackgroundTransparency = 0.4
label.TextColor3 = Color3.fromRGB(255, 215, 0)
label.TextScaled = true
label.Font = Enum.Font.SourceSansBold
label.Text = "Поймано: 0 / " .. GOAL

local coinSound = Instance.new("Sound", workspace)
coinSound.SoundId = "coin"; coinSound.Volume = 1
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Каждые 1.5 сек роняем куб (через Heartbeat — task.spawn не умеет yield)
local RunService = game:GetService("RunService")
local _spawnTimer = 0

local function spawnCube()
    -- Используем хелпер __rbxl_spawn_part — он сразу создаёт примитив
    -- с правильными свойствами (включая anchored=false → реальная гравитация).
    local cube = __rbxl_spawn_part({
        type = "cube",
        x = math.random(-6, 6), y = 14, z = math.random(-6, 6),
        sx = 0.8, sy = 0.8, sz = 0.8,
        color = "#ffcc33",
        anchored = false,   -- падает
        canCollide = true,
    })
    if not cube then return end
    Debris:AddItem(cube, 6)

    local caught = false
    cube.Touched:Connect(function(hit)
        if caught or won then return end
        local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
        if not h then return end
        caught = true
        score = score + 1
        label.Text = "Поймано: " .. score .. " / " .. GOAL
        coinSound:Play()
        cube:Destroy()
        if score >= GOAL then
            won = true
            winSound:Play()
            __rbxl_show_text("Победа! Ты поймал 15 кубов!", 5)
            local px = __rbxl_player_x()
            local py = __rbxl_player_y()
            local pz = __rbxl_player_z()
            __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
        end
    end)
end

RunService.Heartbeat:Connect(function(dt)
    if won then return end
    _spawnTimer = _spawnTimer + (dt or 0.016)
    if _spawnTimer >= 1.5 then
        _spawnTimer = 0
        spawnCube()
    end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 8 — «Беги до финиша»
    // ═══════════════════════════════════════════════════════════════
    'run-to-finish': {
        g8_main: `-- === ИГРА «БЕГИ К ФИНИШУ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local finished = false
local time = 0

__rbxl_show_text("Беги к зелёному финишу — на время!", 3)

-- Секундомер вверху по центру
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
local timerLabel = Instance.new("TextLabel", screenGui)
timerLabel.Size = UDim2.new(0, 220, 0, 60)
timerLabel.Position = UDim2.new(0.5, -110, 0, 20)
timerLabel.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
timerLabel.BackgroundTransparency = 0.4
timerLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
timerLabel.TextScaled = true
timerLabel.Font = Enum.Font.SourceSansBold
timerLabel.Text = "0.0 сек"

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Каждый кадр прибавляем dt к таймеру
RunService.Heartbeat:Connect(function(dt)
    if finished then return end
    time = time + (dt or 0.016)
    -- Округляем до одного знака для отображения
    local rounded = math.floor(time * 10) / 10
    timerLabel.Text = string.format("%.1f сек", rounded)
end)

-- Финиш-зона шлёт BindableEvent
local finishEvent = getEvent("FinishReached")
finishEvent.Event:Connect(function()
    if finished then return end
    finished = true
    local t = math.floor(time * 10) / 10
    winSound:Play()
    __rbxl_show_text("Финиш! Твоё время: " .. string.format("%.1f", t) .. " сек", 6)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g8_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("FinishReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 9 — «Светофор»
    // ═══════════════════════════════════════════════════════════════
    'traffic-light': {
        g9_main: `-- === ИГРА «СВЕТОФОР» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false
local phase = "green"  -- "green" (беги) или "red" (замри)
local phaseTimer = 0
local GREEN_TIME = 3
local RED_TIME = 2.5

__rbxl_show_text("Зелёный — беги! Красный — замри!", 3)

local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 1
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Находим светофор и сразу красим в зелёный
local light = workspace:FindFirstChild("Светофор")
if light then light.Color = Color3.fromRGB(34, 221, 85) end
__rbxl_show_text("ЗЕЛЁНЫЙ — беги!", 1.2)

-- Каждый кадр считаем таймер фазы и проверяем движение
local prevX, prevZ = nil, nil
RunService.Heartbeat:Connect(function(dt)
    if won then return end
    dt = dt or 0.016

    -- Переключение фаз
    phaseTimer = phaseTimer + dt
    if phase == "green" and phaseTimer >= GREEN_TIME then
        phaseTimer = 0
        phase = "red"
        if light then light.Color = Color3.fromRGB(226, 59, 59) end
        __rbxl_show_text("КРАСНЫЙ — замри!", 1.2)
    elseif phase == "red" and phaseTimer >= RED_TIME then
        phaseTimer = 0
        phase = "green"
        if light then light.Color = Color3.fromRGB(34, 221, 85) end
        __rbxl_show_text("ЗЕЛЁНЫЙ — беги!", 1.2)
    end

    -- Если красный и игрок шевелится — респаун
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    if prevX and phase == "red" then
        local dx = px - prevX
        local dz = pz - prevZ
        local moved = math.sqrt(dx*dx + dz*dz)
        if moved / dt > 0.8 then
            player:LoadCharacter()
            loseSound:Play()
            __rbxl_show_text("Двинулся на красный! На старт.", 2)
        end
    end
    prevX, prevZ = px, pz
end)

-- Финиш-зона шлёт BindableEvent
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты дошёл до финиша!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g9_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 10 — «Прыжки на пружинах»
    // ═══════════════════════════════════════════════════════════════
    'spring-jump': (function() {
        const overrides = {
            g10_main: `-- === ИГРА «ПРЫЖОК-ПРУЖИНА» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Прыгай по батутам всё выше!", 3)

local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 1
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Каждый кадр проверяем: упал вниз — на старт
RunService.Heartbeat:Connect(function()
    if won then return end
    local py = __rbxl_player_y()
    if py < -3 then
        player:LoadCharacter()
        loseSound:Play()
    end
end)

-- Финиш-зона шлёт BindableEvent
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты допрыгал до верха!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
            g10_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
        };
        // Скрипт каждого батута — одинаковый код
        const trampScript = `-- === Скрипт батута (Lua) ===
-- Игрок встал на батут — мощный подброс вверх.
local part = script.Parent
local jumpSound = Instance.new("Sound", part)
jumpSound.SoundId = "jump"; jumpSound.Volume = 0.7

local lastBoost = 0
part.Touched:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    -- Не зацикливаем подброс — минимум 0.5с между активациями
    local now = tick()
    if now - lastBoost < 0.5 then return end
    lastBoost = now
    __rbxl_boost_jump(3.2)  -- 3.2 = втрое выше обычного прыжка
    jumpSound:Play()
end)`;
        // Батуты в JS-builder имеют id 4, 5, 6 (после трёх этажей)
        for (const tid of [4, 5, 6]) {
            overrides['g10_tramp_' + tid] = trampScript;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 11 — «Эхо» (нажми кнопку → звук)
    // ═══════════════════════════════════════════════════════════════
    'echo-room': (function() {
        // Звуки и цвета для 6 плиток — должны совпадать с JS-builder
        const tiles = [
            { sound: 'coin',   color: '#e23b3b' },  // 1
            { sound: 'jump',   color: '#f59e0b' },  // 2
            { sound: 'pickup', color: '#facc15' },  // 3
            { sound: 'click',  color: '#22c55e' },  // 4
            { sound: 'hit',    color: '#3b82f6' },  // 5
            { sound: 'coin',   color: '#a855f7' },  // 6
        ];
        const TOTAL = tiles.length;

        const overrides = {
            g11_main: `-- === ИГРА «ЭХО-КОМНАТА» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local stepped = 0
local TOTAL = ${TOTAL}
local won = false

__rbxl_show_text("Наступи на все цветные плитки!", 3)

-- Счётчик в правом верхнем углу
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
local label = Instance.new("TextLabel", screenGui)
label.Size = UDim2.new(0, 240, 0, 50)
label.Position = UDim2.new(1, -260, 0, 20)
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
label.BackgroundTransparency = 0.4
label.TextColor3 = Color3.fromRGB(255, 255, 255)
label.TextScaled = true
label.Font = Enum.Font.SourceSansBold
label.Text = "Плитки: 0 / " .. TOTAL

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Плитка впервые засчитана
local stepEvent = getEvent("EchoStep")
stepEvent.Event:Connect(function()
    stepped = stepped + 1
    label.Text = "Плитки: " .. stepped .. " / " .. TOTAL
    if stepped >= TOTAL then
        __rbxl_show_text("Все плитки звучали! Иди на финиш.", 3)
    end
end)

-- Игрок встал на финиш
local finishEvent = getEvent("EchoFinish")
finishEvent.Event:Connect(function()
    if won then return end
    if stepped < TOTAL then
        __rbxl_show_text("Сначала пройди все " .. TOTAL .. " плиток!", 2)
        return
    end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Эхо-комната пройдена!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
            g11_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent

part.Touched:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    local ev = ReplicatedStorage:FindFirstChild("EchoFinish")
    if ev then ev:Fire() end
end)`,
        };

        // Скрипт каждой звуковой плитки — со своим звуком и цветом
        for (let i = 0; i < TOTAL; i++) {
            const t = tiles[i];
            overrides['g11_tile_' + (i + 1)] = `-- === Скрипт звуковой плитки (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local used = false
local lastSound = 0

local tileSound = Instance.new("Sound", part)
tileSound.SoundId = "${t.sound}"; tileSound.Volume = 0.8

part.Touched:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    -- Звук эхом — каждый раз, но не чаще 0.4с
    local now = tick()
    if now - lastSound > 0.4 then
        lastSound = now
        tileSound:Play()
        -- Вспышка частиц над плиткой
        local pos = part.Position
        __rbxl_spawn_particles("sparks", pos.X, pos.Y + 1, pos.Z, 0.6, 1)
    end
    -- Засчитываем плитку только в первый раз
    if not used then
        used = true
        local ev = ReplicatedStorage:FindFirstChild("EchoStep")
        if ev then ev:Fire() end
    end
end)`;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 12 — «Кодовая дверь»
    // ═══════════════════════════════════════════════════════════════
    'code-door': (function() {
        const overrides = {
            g12_main: `-- === ИГРА «ДВЕРЬ ПО КОДУ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local TweenService = game:GetService("TweenService")
local CODE = { 3, 1, 4, 2 }
local entered = {}
local opened = false
local won = false

__rbxl_show_text("Нажми кнопки в правильном порядке (E)", 4)

local clickSound = Instance.new("Sound", workspace)
clickSound.SoundId = "click"; clickSound.Volume = 0.7
local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 0.8
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

local pressEvent = getEvent("ButtonPress")
pressEvent.Event:Connect(function(num)
    if opened then return end
    clickSound:Play()
    table.insert(entered, num)
    local i = #entered
    if entered[i] ~= CODE[i] then
        -- ошибка — сброс
        entered = {}
        __rbxl_show_text("Неверно! Код сброшен.", 1.5)
        loseSound:Play()
        return
    end
    if #entered == #CODE then
        opened = true
        __rbxl_show_text("Код верный! Дверь открывается.", 3)
        winSound:Play()
        local door = workspace:FindFirstChild("Дверь")
        if door then
            local dp = door.Position
            local goal = { Position = Vector3.new(dp.X, dp.Y + 6, dp.Z) }
            TweenService:Create(door, TweenInfo.new(1.2), goal):Play()
            door.CanCollide = false
        end
    else
        __rbxl_show_text("Верно! Дальше...", 1)
    end
end)

local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    __rbxl_show_text("Победа! Ты разгадал код!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
            g12_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
        };
        // 4 кнопки — каждая шлёт свой номер при нажатии E (если игрок рядом)
        for (let num = 1; num <= 4; num++) {
            overrides['g12_btn_' + num] = `-- === Скрипт кнопки-цифры ${num} (Lua) ===
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local part = script.Parent
local hintVisible = false

-- Подсказка над кнопкой (виден когда игрок рядом)
local hintGui = Instance.new("BillboardGui", part)
hintGui.Size = UDim2.new(4, 0, 1, 0)
hintGui.StudsOffset = Vector3.new(0, 1.5, 0)
hintGui.AlwaysOnTop = true
local label = Instance.new("TextLabel", hintGui)
label.Size = UDim2.new(1, 0, 1, 0)
label.BackgroundTransparency = 1
label.TextColor3 = Color3.fromRGB(255, 255, 255)
label.TextStrokeTransparency = 0
label.TextScaled = true
label.Text = "[E] Нажать ${num}"
label.Visible = false

-- Каждый кадр проверяем дистанцию до игрока
RunService.Heartbeat:Connect(function()
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local dx = part.Position.X - px
    local dz = part.Position.Z - pz
    local dist = math.sqrt(dx*dx + dz*dz)
    local near = dist <= 3
    if near ~= hintVisible then
        hintVisible = near
        label.Visible = near
    end
end)

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if not hintVisible then return end
    if input.KeyCode ~= Enum.KeyCode.E then return end
    local ev = ReplicatedStorage:FindFirstChild("ButtonPress")
    if ev then ev:Fire(${num}) end
end)`;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 13 — «Торговец»
    // ═══════════════════════════════════════════════════════════════
    'trader': {
        g13_main: `-- === ИГРА «ТОРГОВЕЦ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local player = Players.LocalPlayer
local hasKey = false
local won = false

__rbxl_show_text("Поговори с торговцем — нажми E у прилавка", 4)

local pickupSound = Instance.new("Sound", workspace)
pickupSound.SoundId = "pickup"; pickupSound.Volume = 0.8
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Спавним NPC-торговца за прилавком (паритет с JS spawnNpc)
local traderRef = __rbxl_spawn_npc("character-a", 0, 1, 5, "Торговец Боб", 100, 0)

-- Определяем итем "Ключ" в инвентаре и показываем hotbar
__rbxl_inventory_define("key", "Ключ", "#ffd700")

-- Игрок заговорил с торговцем
local talkEvent = getEvent("TalkTrader")
talkEvent.Event:Connect(function()
    if hasKey then
        __rbxl_npc_say(traderRef, "Иди к двери, ключ у тебя!", 3)
        return
    end
    hasKey = true
    __rbxl_npc_say(traderRef, "Привет! Вот тебе ключ от двери. Удачи!", 4)
    __rbxl_inventory_add("key", 1)
    __rbxl_show_text("Ты получил Ключ!", 2)
    pickupSound:Play()
end)

-- Игрок пытается открыть дверь
local openEvent = getEvent("OpenDoor")
openEvent.Event:Connect(function()
    if not __rbxl_inventory_has("key") then
        __rbxl_show_text("Дверь заперта. Нужен ключ от торговца.", 2)
        return
    end
    __rbxl_show_text("Ключ подошёл! Дверь открыта.", 3)
    winSound:Play()
    local door = workspace:FindFirstChild("Дверь")
    if door then
        local dp = door.Position
        local goal = { Position = Vector3.new(dp.X, dp.Y + 6, dp.Z) }
        TweenService:Create(door, TweenInfo.new(1.2), goal):Play()
        door.CanCollide = false
    end
end)

-- Игрок встал на финиш
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    __rbxl_show_text("Победа! Ты прошёл лавку торговца!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g13_counter: `-- === Скрипт прилавка (Lua) ===
-- Подойди и нажми E чтобы поговорить с торговцем.
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local part = script.Parent
local hintVisible = false

RunService.Heartbeat:Connect(function()
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local dx = part.Position.X - px
    local dz = part.Position.Z - pz
    local dist = math.sqrt(dx*dx + dz*dz)
    local near = dist <= 4
    if near ~= hintVisible then
        hintVisible = near
        if near then
            __rbxl_hud_set("g13_counter_hint", "[E] Поговорить с торговцем", 50, 75, "#ffe44a", 20)
        else
            __rbxl_hud_set("g13_counter_hint", nil)
        end
    end
end)

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if not hintVisible then return end
    if input.KeyCode ~= Enum.KeyCode.E then return end
    local ev = ReplicatedStorage:FindFirstChild("TalkTrader")
    if ev then ev:Fire() end
end)`,
        g13_door: `-- === Скрипт двери (Lua) ===
-- Подойди и нажми E чтобы открыть дверь (нужен ключ).
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local part = script.Parent
local hintVisible = false

RunService.Heartbeat:Connect(function()
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local dx = part.Position.X - px
    local dz = part.Position.Z - pz
    local dist = math.sqrt(dx*dx + dz*dz)
    local near = dist <= 4
    if near ~= hintVisible then
        hintVisible = near
        if near then
            __rbxl_hud_set("g13_door_hint", "[E] Открыть дверь", 50, 75, "#ffe44a", 20)
        else
            __rbxl_hud_set("g13_door_hint", nil)
        end
    end
end)

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if not hintVisible then return end
    if input.KeyCode ~= Enum.KeyCode.E then return end
    local ev = ReplicatedStorage:FindFirstChild("OpenDoor")
    if ev then ev:Fire() end
end)`,
        g13_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 14 — «Собери по тегу»
    // ═══════════════════════════════════════════════════════════════
    'collect-by-tag': (function() {
        const TOTAL = 7;
        const overrides = {
            g14_main: `-- === ИГРА «СОБЕРИ ПО ТЕГАМ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local TOTAL = ${TOTAL}
local won = false

__rbxl_show_text("Собери все ЖЁЛТЫЕ звёзды!", 3)

-- Счётчик в правом верхнем углу
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
local label = Instance.new("TextLabel", screenGui)
label.Size = UDim2.new(0, 220, 0, 50)
label.Position = UDim2.new(1, -240, 0, 20)
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
label.BackgroundTransparency = 0.4
label.TextColor3 = Color3.fromRGB(255, 215, 0)
label.TextScaled = true
label.Font = Enum.Font.SourceSansBold
label.Text = "Звёзды: 0 / " .. TOTAL

local coinSound = Instance.new("Sound", workspace)
coinSound.SoundId = "coin"; coinSound.Volume = 0.8
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Помечаем все звёзды тегом 'звезда' (с небольшой задержкой —
-- скрипты звёзд должны успеть запуститься и зарегистрировать part).
task.delay(0.2, function()
    for i = 1, TOTAL do
        local star = workspace:FindFirstChild("Звезда_" .. i)
        if star then CollectionService:AddTag(star, "звезда") end
    end
end)

-- Звёзды сообщают о сборе. Главный скрипт считает оставшиеся через
-- CollectionService:GetTagged — паритет с JS game.scene.getTagged.
local collectEvent = getEvent("StarCollected")
collectEvent.Event:Connect(function()
    if won then return end
    local left = #CollectionService:GetTagged("звезда")
    label.Text = "Звёзды: " .. (TOTAL - left) .. " / " .. TOTAL
    coinSound:Play()
    if left == 0 then
        won = true
        winSound:Play()
        __rbxl_show_text("Победа! Все звёзды собраны!", 5)
        local px = __rbxl_player_x()
        local py = __rbxl_player_y()
        local pz = __rbxl_player_z()
        __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
    end
end)`,
        };
        // Скрипт каждой звезды — одинаковый: на touch → untag + destroy + Fire
        const starScript = `-- === Скрипт звезды (Lua) ===
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local picked = false

part.Touched:Connect(function(hit)
    if picked then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    picked = true
    -- Снимаем тег и удаляем звезду, потом шлём событие.
    CollectionService:RemoveTag(part, "звезда")
    part:Destroy()
    local ev = ReplicatedStorage:FindFirstChild("StarCollected")
    if ev then ev:Fire() end
end)`;
        for (let i = 1; i <= TOTAL; i++) {
            overrides['g14_star_' + i] = starScript;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 15 — «Тир»
    // ═══════════════════════════════════════════════════════════════
    'shooting-range': (function() {
        // Мишени имеют id 2, 4, 6, 8, 10, 12, 14, 16 (постамент → нечётный, мишень → чётный)
        const TARGET_IDS = [2, 4, 6, 8, 10, 12, 14, 16];
        const TOTAL = TARGET_IDS.length;
        const overrides = {
            g15_main: `-- === ИГРА «ТИР» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local score = 0
local TOTAL = ${TOTAL}
local won = false

__rbxl_show_text("Кликай по красным мишеням!", 3)

-- Счётчик в правом верхнем углу
local screenGui = Instance.new("ScreenGui", player.PlayerGui)
local label = Instance.new("TextLabel", screenGui)
label.Size = UDim2.new(0, 220, 0, 50)
label.Position = UDim2.new(1, -240, 0, 20)
label.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
label.BackgroundTransparency = 0.4
label.TextColor3 = Color3.fromRGB(255, 100, 100)
label.TextScaled = true
label.Font = Enum.Font.SourceSansBold
label.Text = "Мишени: 0 / " .. TOTAL

local hitSound = Instance.new("Sound", workspace)
hitSound.SoundId = "hit"; hitSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

local hitEvent = getEvent("TargetHit")
hitEvent.Event:Connect(function()
    if won then return end
    score = score + 1
    label.Text = "Мишени: " .. score .. " / " .. TOTAL
    hitSound:Play()
    if score >= TOTAL then
        won = true
        winSound:Play()
        __rbxl_show_text("Победа! Все мишени выбиты!", 5)
        local px = __rbxl_player_x()
        local py = __rbxl_player_y()
        local pz = __rbxl_player_z()
        __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
    end
end)`,
        };
        // Скрипт каждой мишени — ClickDetector + взрыв искр + сообщение
        const targetScript = `-- === Скрипт мишени (Lua) ===
-- Клик ЛКМ по мишени = выстрел. ClickDetector ловит клик в 3D.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local hit = false

local clickDet = Instance.new("ClickDetector", part)
clickDet.MaxActivationDistance = 50

clickDet.MouseClick:Connect(function()
    if hit then return end
    hit = true
    -- Взрыв искр на месте мишени
    local pos = part.Position
    __rbxl_spawn_particles("explosion", pos.X, pos.Y, pos.Z, 0.5, 1)
    -- Сообщаем главному скрипту
    local ev = ReplicatedStorage:FindFirstChild("TargetHit")
    if ev then ev:Fire() end
    -- Мишень исчезает
    part:Destroy()
end)`;
        for (const tid of TARGET_IDS) {
            overrides['g15_target_' + tid] = targetScript;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 16 — «Лавовый пол»
    // ═══════════════════════════════════════════════════════════════
    'lava-floor': {
        g16_main: `-- === ИГРА «ЛАВА-ПОЛ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Прыгай по островкам! Лава жжёт!", 3)

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Если упал в озеро вниз — респаун
RunService.Heartbeat:Connect(function()
    if won then return end
    local py = __rbxl_player_y()
    if py < -2 then
        player:LoadCharacter()
    end
end)

-- Финиш сообщает о победе
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты перебрался через лаву!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g16_lava: `-- === Скрипт лавы (Lua) ===
-- Игрок коснулся лавы — урон. У damage есть защита (i-frames),
-- так что урон не каждый кадр, а раз в ~0.5 секунды.
-- ВАЖНО: проверяем что игрок НЕ над островком (по X/Z),
-- иначе урон срабатывает на островке из-за пересечения AABB.
local RunService = game:GetService("RunService")
local part = script.Parent
local hitSound = Instance.new("Sound", part)
hitSound.SoundId = "hit"; hitSound.Volume = 0.7

-- Координаты островков (из builder): { x, z }
local ISLES = {
    {0, 5}, {3, 9}, {-2, 13}, {2, 17}, {-3, 21}, {1, 25},
}
local ISLE_HW = 1.4  -- половина ширины островка sx=2.4/2 + небольшой запас
local FINISH_X, FINISH_Z = -0.5, 29
local FINISH_HW, FINISH_HD = 3, 2.5

local function isOverSafeSpot()
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    -- Островки
    for _, isle in ipairs(ISLES) do
        if math.abs(px - isle[1]) <= ISLE_HW
            and math.abs(pz - isle[2]) <= ISLE_HW then
            return true
        end
    end
    -- Финишная площадка
    if math.abs(px - FINISH_X) <= FINISH_HW
        and math.abs(pz - FINISH_Z) <= FINISH_HD then
        return true
    end
    return false
end

-- Проверяем каждый кадр пока игрок касается лавы — наносим урон если он
-- НЕ над безопасным местом. Touched нам не нужен — лучше Heartbeat-проверка.
local inLava = false
part.Touched:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    inLava = true
end)
part.TouchEnded:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    inLava = false
end)

RunService.Heartbeat:Connect(function()
    if not inLava then return end
    if isOverSafeSpot() then return end  -- стоит над островком/финишем
    __rbxl_damage_player(20)
    hitSound:Play()
end)`,
        g16_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 17 — «Ключ от сундука»
    // ═══════════════════════════════════════════════════════════════
    'key-chest': {
        g17_main: `-- === ИГРА «КЛЮЧ И СУНДУК» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local won = false

__rbxl_show_text("Найди ключ и открой сундук!", 3)

local pickupSound = Instance.new("Sound", workspace)
pickupSound.SoundId = "pickup"; pickupSound.Volume = 0.8
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1
local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 0.6

-- Определяем итем "Ключ" в инвентаре (показывает иконку в hotbar)
__rbxl_inventory_define("key", "Ключ", "#ffd700")

-- Игрок подобрал ключ
local takeEvent = getEvent("TakeKey")
takeEvent.Event:Connect(function()
    __rbxl_inventory_add("key", 1)
    __rbxl_show_text("Ты нашёл Ключ!", 2)
    pickupSound:Play()
end)

-- Игрок пытается открыть сундук
local openEvent = getEvent("OpenChest")
openEvent.Event:Connect(function()
    if won then return end
    if not __rbxl_inventory_has("key") then
        __rbxl_show_text("Сундук заперт. Сначала найди ключ.", 2)
        loseSound:Play()
        return
    end
    won = true
    __rbxl_show_text("Победа! Сундук открыт — там сокровище!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g17_key: `-- === Скрипт ключа (Lua) ===
-- При касании игроком — отправляем событие и удаляем ключ.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local taken = false

part.Touched:Connect(function(hit)
    if taken then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    taken = true
    local ev = ReplicatedStorage:FindFirstChild("TakeKey")
    if ev then ev:Fire() end
    part:Destroy()
end)`,
        g17_chest: `-- === Скрипт сундука (Lua) ===
-- Подойти и нажать E чтобы открыть.
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local part = script.Parent
local hintVisible = false

RunService.Heartbeat:Connect(function()
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local dx = part.Position.X - px
    local dz = part.Position.Z - pz
    local dist = math.sqrt(dx*dx + dz*dz)
    local near = dist <= 4
    if near ~= hintVisible then
        hintVisible = near
        if near then
            __rbxl_hud_set("g17_chest_hint", "[E] Открыть сундук", 50, 75, "#ffe44a", 20)
        else
            __rbxl_hud_set("g17_chest_hint", nil)
        end
    end
end)

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if not hintVisible then return end
    if input.KeyCode ~= Enum.KeyCode.E then return end
    local ev = ReplicatedStorage:FindFirstChild("OpenChest")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 18 — «Качели»
    // ═══════════════════════════════════════════════════════════════
    'swing': {
        g18_main: `-- === ИГРА «КАЧЕЛИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Запрыгни на качели и прокатись!", 3)

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Раскачиваем качели туда-сюда через изменение Position.Z.
-- WaitForChild может зависнуть — берём напрямую с задержкой.
local swing = nil
local startZ = 0

task.delay(0.2, function()
    swing = workspace:FindFirstChild("Качели")
    if swing then
        startZ = swing.Position.Z
    end
end)

local elapsed = 0
RunService.Heartbeat:Connect(function(dt)
    if won then return end
    if not swing then return end
    elapsed = elapsed + (dt or 0.016)
    -- Синусоидальное качание с амплитудой 4 и периодом ~2.8 сек
    local amp = 4
    local period = 2.8
    local offsetZ = amp * math.sin(elapsed * 2 * math.pi / period)
    local pos = swing.Position
    swing.Position = Vector3.new(pos.X, pos.Y, startZ + offsetZ)

    -- Если упал — респаун
    local py = __rbxl_player_y()
    if py < -3 then
        player:LoadCharacter()
    end
end)

-- Финиш сообщает о победе
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты перебрался на качелях!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g18_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 19 — «Лифт»
    // ═══════════════════════════════════════════════════════════════
    'elevator': {
        g19_main: `-- === ИГРА «ЛИФТ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Встань на синий лифт — он повезёт наверх", 3)

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Лифт ездит вверх-вниз. WaitForChild зависает, поэтому FindFirstChild
-- с задержкой через task.delay.
local lift = nil
local startY = 1
local TOP_Y = 12.3
local PERIOD = 7  -- полный цикл вниз→вверх→вниз (3.5с вверх + 3.5с вниз)
local elapsed = 0

task.delay(0.2, function()
    lift = workspace:FindFirstChild("Лифт")
    if lift then startY = lift.Position.Y end
end)

RunService.Heartbeat:Connect(function(dt)
    if won then return end
    dt = dt or 0.016
    -- Лифт двигается
    if lift then
        elapsed = elapsed + dt
        -- Yo-yo: 0..PERIOD/2 — вверх, PERIOD/2..PERIOD — вниз
        local t = (elapsed % PERIOD) / PERIOD
        local k
        if t < 0.5 then
            k = t * 2  -- 0..1
        else
            k = (1 - t) * 2  -- 1..0
        end
        local y = startY + (TOP_Y - startY) * k
        local pos = lift.Position
        lift.Position = Vector3.new(pos.X, y, pos.Z)
    end
    -- Падение
    local py = __rbxl_player_y()
    if py < -3 then
        player:LoadCharacter()
    end
end)

-- Финиш
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты поднялся на лифте!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g19_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 20 — «Имена врагов»
    // ═══════════════════════════════════════════════════════════════
    'enemy-names': {
        g20_main: `-- === ИГРА «ИМЕНА НАД ВРАГАМИ» — главный скрипт (Lua) ===
local UserInputService = game:GetService("UserInputService")

__rbxl_show_text("Победи всех врагов! Кликай по ним", 3)

local hitSound = Instance.new("Sound", workspace)
hitSound.SoundId = "hit"; hitSound.Volume = 0.6
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Данные врагов: имя, позиция, HP
local enemies = {
    { name = "Гоблин",  x = -5, z = 3,  hp = 60,  maxHp = 60,  ref = nil },
    { name = "Скелет",  x = 4,  z = 5,  hp = 80,  maxHp = 80,  ref = nil },
    { name = "Орк",     x = 0,  z = 8,  hp = 100, maxHp = 100, ref = nil },
}

local alive = #enemies
local won = false

-- Спавним всех врагов и метки над ними
for i, e in ipairs(enemies) do
    e.ref = __rbxl_spawn_npc("character-b", e.x, 1, e.z, e.name, e.hp, 0)
    __rbxl_set_label(e.ref, e.name .. "  HP: " .. e.hp, "#ff5555", 2.5)
    -- Callback на смерть NPC
    __rbxl_npc_on_death(e.ref, function()
        if e._dead then return end
        e._dead = true
        __rbxl_clear_label(e.ref)
        alive = alive - 1
        hitSound:Play()
        if alive <= 0 and not won then
            won = true
            winSound:Play()
            __rbxl_show_text("Победа! Все враги повержены!", 5)
            local px = __rbxl_player_x()
            local py = __rbxl_player_y()
            local pz = __rbxl_player_z()
            __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
        end
    end)
end

-- Клик по конкретному NPC (как в Тире — pick по 3D-объекту).
-- BabylonScene выполняет raycast при ЛКМ и шлёт click с target=NPC.
-- Регистрируем callback для каждого врага по его локальному ref.
for _, e in ipairs(enemies) do
    local enemy = e  -- замыкание
    __rbxl_npc_on_click(enemy.ref, function()
        if enemy._dead or won then return end
        enemy.hp = enemy.hp - 30
        if enemy.hp < 0 then enemy.hp = 0 end
        __rbxl_npc_damage(enemy.ref, 30)
        __rbxl_set_label(enemy.ref, enemy.name .. "  HP: " .. enemy.hp, "#ff5555", 2.5)
        __rbxl_spawn_particles("sparks", enemy.x, 2, enemy.z, 0.4, 1)
        hitSound:Play()
    end)
end`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 21 — «Догонялки»
    // ═══════════════════════════════════════════════════════════════
    'chaser': {
        g21_main: `-- === ИГРА «ПРЕСЛЕДОВАТЕЛЬ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Убегай от врага! Добеги до укрытия!", 3)

local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Спавним NPC-преследователя (speed=4, follow за игроком)
local enemyRef = __rbxl_spawn_npc("character-b", 0, 1, -3, "Охотник", 100, 4)
-- Велим NPC следовать за игроком
__rbxl_npc_follow(enemyRef, "player")

-- Каждый кадр проверяем — не догнал ли враг
local lastCaughtTime = 0
RunService.Heartbeat:Connect(function()
    if won then return end
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local ex = __rbxl_npc_x(enemyRef)
    local ez = __rbxl_npc_z(enemyRef)
    -- Если позиции NPC ещё не пришли (ex=0,ez=0 = до спавна) — пропускаем
    if ex == 0 and ez == 0 then return end
    local dx = px - ex
    local dz = pz - ez
    local dist = math.sqrt(dx*dx + dz*dz)
    if dist < 1.6 then
        local now = tick()
        if now - lastCaughtTime > 2 then
            lastCaughtTime = now
            player:LoadCharacter()
            __rbxl_show_text("Пойман! Беги снова!", 2)
            loseSound:Play()
        end
    end
end)

-- Финиш сообщает о победе
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    __rbxl_npc_stop(enemyRef)
    winSound:Play()
    __rbxl_show_text("Победа! Ты убежал от врага!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g21_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 22 — «Опасная зона»
    // ═══════════════════════════════════════════════════════════════
    'danger-zone': {
        g22_main: `-- === ИГРА «ЗОНА ОПАСНОСТИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local RunService = game:GetService("RunService")
local inZone = false
local won = false
local damageTimer = 0

__rbxl_show_text("Пробеги через красную зону к финишу!", 3)

local hitSound = Instance.new("Sound", workspace)
hitSound.SoundId = "hit"; hitSound.Volume = 0.6
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Слушатели событий зоны
local enterEvent = getEvent("ZoneEnter")
enterEvent.Event:Connect(function()
    inZone = true
    __rbxl_show_text("Опасно! Беги быстрее!", 1.5)
end)
local leaveEvent = getEvent("ZoneLeave")
leaveEvent.Event:Connect(function()
    inZone = false
end)

-- Урон каждые 0.6с пока игрок в зоне
RunService.Heartbeat:Connect(function(dt)
    if won then return end
    if not inZone then return end
    damageTimer = damageTimer + (dt or 0.016)
    if damageTimer >= 0.6 then
        damageTimer = 0
        __rbxl_damage_player(12)
        hitSound:Play()
    end
end)

-- Финиш сообщает о победе
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты прошёл зону опасности!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g22_zone: `-- === Скрипт зоны опасности (Lua) ===
-- Touched при входе, TouchEnded при выходе.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent

part.Touched:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    local ev = ReplicatedStorage:FindFirstChild("ZoneEnter")
    if ev then ev:Fire() end
end)
part.TouchEnded:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    local ev = ReplicatedStorage:FindFirstChild("ZoneLeave")
    if ev then ev:Fire() end
end)`,
        g22_heal: `-- === Скрипт аптечки (Lua) ===
local part = script.Parent
local taken = false

part.Touched:Connect(function(hit)
    if taken then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    taken = true
    -- Лечим на 60 HP (через damage с отрицательным значением неудобно;
    -- используем напрямую player.Health прибавлением).
    __rbxl_heal_player(60)
    __rbxl_show_text("+60 HP", 1.5)
    local pickupSound = Instance.new("Sound", part)
    pickupSound.SoundId = "pickup"; pickupSound.Volume = 0.8
    pickupSound:Play()
    part:Destroy()
end)`,
        g22_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 23 — «3 переключателя»
    // ═══════════════════════════════════════════════════════════════
    'switches': (function() {
        const overrides = {
            g23_main: `-- === ИГРА «ПЕРЕКЛЮЧАТЕЛИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local TweenService = game:GetService("TweenService")
local ORDER = { 2, 3, 1 }  -- правильный порядок рычагов
local pressed = {}
local opened = false
local won = false

__rbxl_show_text("Дёрни рычаги в нужном порядке (E)", 4)

local clickSound = Instance.new("Sound", workspace)
clickSound.SoundId = "click"; clickSound.Volume = 0.7
local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Рычаги шлют ev:Fire(num) с номером
local leverEvent = getEvent("LeverPulled")
leverEvent.Event:Connect(function(num)
    if opened then return end
    clickSound:Play()
    table.insert(pressed, num)
    local i = #pressed
    if pressed[i] ~= ORDER[i] then
        pressed = {}
        __rbxl_show_text("Неверно! Рычаги сброшены.", 1.5)
        loseSound:Play()
        return
    end
    if #pressed == #ORDER then
        opened = true
        __rbxl_show_text("Верно! Дверь открыта.", 3)
        winSound:Play()
        local door = workspace:FindFirstChild("Дверь")
        if door then
            local dp = door.Position
            local goal = { Position = Vector3.new(dp.X, dp.Y + 6, dp.Z) }
            TweenService:Create(door, TweenInfo.new(1.2), goal):Play()
            door.CanCollide = false
        end
    else
        __rbxl_show_text("Так держать!", 1)
    end
end)

-- Финиш
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    __rbxl_show_text("Победа! Ты разгадал порядок!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
            g23_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
        };
        // 3 рычага — каждый ждёт E когда игрок рядом, шлёт свой номер
        for (let n = 1; n <= 3; n++) {
            overrides['g23_lever_' + n] = `-- === Скрипт рычага ${n} (Lua) ===
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local part = script.Parent
local hintVisible = false

RunService.Heartbeat:Connect(function()
    local px = __rbxl_player_x()
    local pz = __rbxl_player_z()
    local dx = part.Position.X - px
    local dz = part.Position.Z - pz
    local dist = math.sqrt(dx*dx + dz*dz)
    local near = dist <= 3
    if near ~= hintVisible then
        hintVisible = near
        if near then
            __rbxl_hud_set("g23_lever_${n}_hint", "[E] Дёрнуть рычаг ${n}", 50, 75, "#ffe44a", 20)
        else
            __rbxl_hud_set("g23_lever_${n}_hint", nil)
        end
    end
end)

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if not hintVisible then return end
    if input.KeyCode ~= Enum.KeyCode.E then return end
    local ev = ReplicatedStorage:FindFirstChild("LeverPulled")
    if ev then ev:Fire(${n}) end
end)`;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 24 — «Падающий мост»
    // ═══════════════════════════════════════════════════════════════
    'falling-bridge': (function() {
        const TILES = 18;
        const overrides = {
            g24_main: `-- === ИГРА «ПАДАЮЩИЙ МОСТ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false

__rbxl_show_text("Беги по мосту — доски рушатся!", 3)

local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Респаун при падении в пропасть
RunService.Heartbeat:Connect(function()
    if won then return end
    local py = __rbxl_player_y()
    if py < -3 then
        player:LoadCharacter()
        __rbxl_show_text("Упал в пропасть! Снова.", 1.5)
        loseSound:Play()
    end
end)

-- Финиш сообщает о победе
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    winSound:Play()
    __rbxl_show_text("Победа! Ты перебежал мост!", 5)
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
            g24_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
        };
        // 18 досок — каждая при касании играет click и исчезает через 1с
        const plankScript = `-- === Скрипт доски моста (Lua) ===
local Debris = game:GetService("Debris")
local part = script.Parent
local cracking = false

local clickSound = Instance.new("Sound", part)
clickSound.SoundId = "click"; clickSound.Volume = 0.5

part.Touched:Connect(function(hit)
    if cracking then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    cracking = true
    clickSound:Play()
    -- через 1 секунду доска пропадает
    Debris:AddItem(part, 1)
end)`;
        for (let i = 1; i <= TILES; i++) {
            overrides['g24_plank_' + i] = plankScript;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 25 — «Облёт камеры»
    // ═══════════════════════════════════════════════════════════════
    'flyby-camera': {
        g25_main: `-- === ИГРА «КАМЕРА-ОБЛЁТ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local won = false

-- При старте — облёт уровня камерой по точкам.
-- 1-й arg — путь камеры (4 точки x,y,z),
-- 2-й — длительность одного отрезка,
-- 3-й — куда камера смотрит в каждой точке (тоже 4 точки).
__rbxl_camera_cutscene(
    "0,18,-10, 12,12,8, -12,12,18, 0,10,28", 1.8,
    "0,2,8, 0,2,14, 0,2,20, 0,2,27"
)

local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Когда облёт закончился — отдаём камеру игроку и пишем подсказку
__rbxl_on_cutscene_done(function()
    __rbxl_show_text("Вперёд, к зелёному финишу!", 3)
end)

-- Финиш сообщает о победе
local winEvent = getEvent("WinReached")
winEvent.Event:Connect(function()
    if won then return end
    won = true
    __rbxl_show_text("Победа! Уровень пройден!", 5)
    winSound:Play()
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
end)`,
        g25_finish: `-- === Скрипт финиша (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local fired = false

part.Touched:Connect(function(hit)
    if fired then return end
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    fired = true
    local ev = ReplicatedStorage:FindFirstChild("WinReached")
    if ev then ev:Fire() end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 26 — «Магнит монет»
    // ═══════════════════════════════════════════════════════════════
    'coin-magnet': (function() {
        const TOTAL = 8;
        const overrides = {
            g26_main: `-- === ИГРА «МАГНИТ МОНЕТ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}

local TOTAL = ${TOTAL}
local score = 0

__rbxl_score_set(0)
__rbxl_show_text("Подходи к монеткам — они притянутся!", 3)

local coinSound = Instance.new("Sound", workspace)
coinSound.SoundId = "coin"; coinSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1

-- Монетки шлют ev:Fire() при сборе
local coinEvent = getEvent("CoinCollected")
coinEvent.Event:Connect(function()
    score = score + 1
    __rbxl_score_set(score)
    coinSound:Play()
    if score >= TOTAL then
        winSound:Play()
        __rbxl_show_text("Победа! Все монетки собраны!", 5)
        local px = __rbxl_player_x()
        local py = __rbxl_player_y()
        local pz = __rbxl_player_z()
        __rbxl_spawn_particles("confetti", px, py + 3, pz, 3, 3)
    end
end)`,
        };
        // 8 монеток — каждая со своим Heartbeat: при dist<6 летит к игроку, при dist<1.2 собрана
        const coinScript = `-- === Скрипт магнитной монетки (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local part = script.Parent
local flying = false
local taken = false

RunService.Heartbeat:Connect(function()
    if taken then return end
    local cp = part.Position
    local px = __rbxl_player_x()
    local py = __rbxl_player_y()
    local pz = __rbxl_player_z()
    -- ждём пока позиция игрока придёт
    if px == 0 and py == 0 and pz == 0 then return end
    local dx = px - cp.X
    local dz = pz - cp.Z
    local dist = math.sqrt(dx*dx + dz*dz)

    if dist < 1.2 then
        taken = true
        local ev = ReplicatedStorage:FindFirstChild("CoinCollected")
        if ev then ev:Fire() end
        part:Destroy()
        return
    end
    if not flying and dist < 6 then
        flying = true
        local goal = { Position = Vector3.new(px, py + 1, pz) }
        TweenService:Create(part, TweenInfo.new(0.5), goal):Play()
    end
end)`;
        for (let i = 1; i <= TOTAL; i++) {
            overrides['g26_coin_' + i] = coinScript;
        }
        return overrides;
    })(),

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 27 — «Двойной прыжок»
    // ═══════════════════════════════════════════════════════════════
    'double-jump': {
        g27_main: `-- === ИГРА «ДВОЙНОЙ ПРЫЖОК» (Lua) ===
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")

local function setupDoubleJump(player)
    local jumpsLeft = 2
    local char = player.Character or player.CharacterAdded:Wait()
    local h = char:WaitForChild("Humanoid")

    -- Восстанавливаем прыжки при касании земли
    h.StateChanged:Connect(function(_, newState)
        if newState == Enum.HumanoidStateType.Landed then
            jumpsLeft = 2
        end
    end)

    UserInputService.InputBegan:Connect(function(input, gp)
        if gp then return end
        if input.KeyCode == Enum.KeyCode.Space and jumpsLeft > 0 then
            jumpsLeft = jumpsLeft - 1
            if jumpsLeft == 1 then
                local hrp = char:FindFirstChild("HumanoidRootPart")
                if hrp then
                    hrp.Velocity = Vector3.new(hrp.Velocity.X, 50, hrp.Velocity.Z)
                end
            end
        end
    end)
end

Players.PlayerAdded:Connect(setupDoubleJump)
for _, p in ipairs(Players:GetPlayers()) do setupDoubleJump(p) end
print("Жми Space дважды — двойной прыжок!")`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 28 — «Призрачные стены»
    // ═══════════════════════════════════════════════════════════════
    'ghost-walls': {
        g28_main: `-- === ИГРА «ПРИЗРАЧНЫЕ СТЕНЫ» (Lua) ===
print("Некоторые стены — призрачные. Найди проход!")`,
        g28_ghost: `-- === Скрипт призрачной стены (Lua) ===
local part = script.Parent
part.CanCollide = false
part.Transparency = 0.5`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 29 — «Магазин»
    // ═══════════════════════════════════════════════════════════════
    'shop': {
        g29_main: `-- === ИГРА «МАГАЗИН» (Lua) ===
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
    local stats = Instance.new("Folder", player); stats.Name = "leaderstats"
    local coins = Instance.new("IntValue", stats); coins.Name = "Монеты"; coins.Value = 20
end)
for _, p in ipairs(Players:GetPlayers()) do
    if not p:FindFirstChild("leaderstats") then
        local stats = Instance.new("Folder", p); stats.Name = "leaderstats"
        local coins = Instance.new("IntValue", stats); coins.Name = "Монеты"; coins.Value = 20
    end
end
print("У тебя 20 монет — купи что-нибудь!")`,
        g29_item: `-- === Скрипт товара (Lua) ===
local Players = game:GetService("Players")
local part = script.Parent
local price = part:GetAttribute("Price") or 5
local bought = false

part.Touched:Connect(function(hit)
    if bought then return end
    local player = Players:GetPlayerFromCharacter(hit.Parent)
    if not player then return end
    local stats = player:FindFirstChild("leaderstats")
    if not stats then return end
    if stats['Монеты'].Value >= price then
        stats['Монеты'].Value = stats['Монеты'].Value - price
        bought = true
        print("Куплено! Цена: " .. price)
        part.Transparency = 0.7
    else
        print("Не хватает! Нужно " .. price .. " монет")
    end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРА 30 — «Квесты»
    // ═══════════════════════════════════════════════════════════════
    'quest-tasks': {
        g30_main: `-- === ИГРА «КВЕСТЫ» (Lua) ===
${SNIPPET_BROADCAST}

local quests = {
    { name = "Собери 5 ягод", goal = 5, current = 0 },
    { name = "Победи врага",  goal = 1, current = 0 },
    { name = "Дойди до башни", goal = 1, current = 0 },
}

print("Квесты:")
for i, q in ipairs(quests) do print("  " .. i .. ". " .. q.name) end

local ev = getEvent("QuestProgress")
ev.Event:Connect(function(idx, amount)
    quests[idx].current = quests[idx].current + (amount or 1)
    local q = quests[idx]
    print(q.name .. ": " .. q.current .. "/" .. q.goal)
    if q.current >= q.goal then
        print("Квест выполнен: " .. q.name)
    end
end)`,
    },

    // ═══════════════════════════════════════════════════════════════
    // ИГРЫ 31-50: явных Lua-версий пока нет.
    // buildGameProject в docsGamesBuilders.js использует generateFallbackLua
    // (главный скрипт → показ подсказки + слушает FinishReached →
    //  победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;
    //  остальные target-скрипты → красят примитив на касание).
    // Это даёт «хоть что-то рабочее» в любой игре до того как напишем
    // полноценный Lua-скрипт. Когда дописываем игру — добавляем сюда явный override.
    'clicker': { g46_main: simpleClicker() },
};

// ══════════════════════════════════════════════════════════════════
// Хелперы для генерации часто повторяющихся скриптов
// ══════════════════════════════════════════════════════════════════

/** Возвращает Lua-код скрипта монетки. */
function makeCoinScript() {
    return `-- === Скрипт монетки (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent

part.Touched:Connect(function(hit)
    local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
    if not h then return end
    local ev = ReplicatedStorage:FindFirstChild("CoinCollected")
    if ev then ev:Fire() end
    part:Destroy()
end)`;
}

/** Кликер. */
function simpleClicker() {
    return `-- === КЛИКЕР (Lua) ===
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")

Players.PlayerAdded:Connect(function(player)
    local stats = Instance.new("Folder", player); stats.Name = "leaderstats"
    local cnt = Instance.new("IntValue", stats); cnt.Name = "Клики"; cnt.Value = 0
end)
for _, p in ipairs(Players:GetPlayers()) do
    if not p:FindFirstChild("leaderstats") then
        local stats = Instance.new("Folder", p); stats.Name = "leaderstats"
        local cnt = Instance.new("IntValue", stats); cnt.Name = "Клики"; cnt.Value = 0
    end
end

local function onClick(player)
    local stats = player:FindFirstChild("leaderstats")
    if stats and stats:FindFirstChild("Клики") then
        stats['Клики'].Value = stats['Клики'].Value + 1
    end
end

UserInputService.InputBegan:Connect(function(input, gp)
    if gp then return end
    if input.UserInputType == Enum.UserInputType.MouseButton1 then
        onClick(Players.LocalPlayer)
    end
end)
print("Кликай ЛКМ — копи клики!")`;
}
