From 901c249c297f3dc92e177f80b60885e4349b71b7 Mon Sep 17 00:00:00 2001 From: min Date: Tue, 9 Jun 2026 22:11:15 +0300 Subject: [PATCH] =?UTF-8?q?docs(29)=20+=20feat(g31):=20=C2=AB=D0=97=D0=B0?= =?UTF-8?q?=D1=89=D0=B8=D1=82=D0=B0=20=D0=B1=D0=B0=D0=B7=D1=8B=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit g30 docs CodeBoth 4 скрипта. g31: - killed counter (GOAL=12) + leaked (MAX_LEAK=5) - Heartbeat spawn враг каждые 2с в (random(-8,8), 1, 38), spawn 'character-b' speed 2.5 - task.delay 0.3 npc.moveTo(0, 2) — к базе - __rbxl_npc_on_click(ref, fn) → шлёт ref в общий BindableEvent - При клике главный скрипт проверяет dist<5, наносит урон - Каждые 0.4с проверка прорыва (z<4) → leaked++ + lose sound - 12 убитых → 'Победа!' + confetti - 5 прорывов → 'База разрушена!' Shim: __rbxl_npc_moveto/__rbxl_npc_remove. --- src/community/docsGamesBuildersLua.js | 124 +++++++++++++++++++++++++- src/community/docsLessons.jsx | 16 ++-- src/editor/engine/lua/RobloxShim.js | 9 ++ 3 files changed, 140 insertions(+), 9 deletions(-) diff --git a/src/community/docsGamesBuildersLua.js b/src/community/docsGamesBuildersLua.js index 84cfddd..8ab55dc 100644 --- a/src/community/docsGamesBuildersLua.js +++ b/src/community/docsGamesBuildersLua.js @@ -2734,7 +2734,129 @@ end)`, }, // ═══════════════════════════════════════════════════════════════ - // ИГРЫ 31-50: явных Lua-версий пока нет. + // ИГРА 31 — «Защита базы» + // ═══════════════════════════════════════════════════════════════ + 'base-defense': { + g31_main: `-- === ИГРА «ЗАЩИТА БАЗЫ» — главный скрипт (Lua) === +${SNIPPET_BROADCAST} + +local RunService = game:GetService("RunService") +local UserInputService = game:GetService("UserInputService") + +local killed = 0 +local leaked = 0 +local total = 0 +local over = false +local GOAL = 12 +local MAX_LEAK = 5 + +__rbxl_score_set(0) +__rbxl_show_text("Защити базу! Кликай по врагам", 3) + +local hitSound = Instance.new("Sound", workspace) +hitSound.SoundId = "hit"; hitSound.Volume = 0.6 +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 + +-- Все живые враги: { ref, dead } +local enemies = {} + +-- Клик по NPC (target.kind=npc) — наносим урон ближайшему в радиусе 5 +local clickEvent = getEvent("EnemyClicked") +clickEvent.Event:Connect(function(localRef) + if over then return end + for _, e in ipairs(enemies) do + if not e.dead and e.ref == localRef then + -- Проверка расстояния (в радиусе 5) + local px = __rbxl_player_x() + local pz = __rbxl_player_z() + local ex = __rbxl_npc_x(e.ref) + local ez = __rbxl_npc_z(e.ref) + local dx = px - ex + local dz = pz - ez + local dist = math.sqrt(dx*dx + dz*dz) + if dist < 5 then + e.dead = true + __rbxl_spawn_particles("explosion", ex, 2, ez, 0.4, 1) + __rbxl_npc_remove(e.ref) + hitSound:Play() + killed = killed + 1 + __rbxl_score_set(killed) + if killed >= GOAL and not over then + over = true + winSound:Play() + __rbxl_show_text("Победа! База защищена!", 5) + local px2 = __rbxl_player_x() + local py2 = __rbxl_player_y() + local pz2 = __rbxl_player_z() + __rbxl_spawn_particles("confetti", px2, py2 + 3, pz2, 3, 3) + end + end + return + end + end +end) + +-- Регистрируем общий callback на клик по NPC — он шлёт ref в общий event +-- (фейерим один раз — при появлении каждого врага зовём __rbxl_npc_on_click) + +-- Спавн врага каждые 2 секунды +local spawnTimer = 0 +RunService.Heartbeat:Connect(function(dt) + if over then return end + if total >= GOAL + MAX_LEAK then return end + spawnTimer = spawnTimer + dt + if spawnTimer >= 2 then + spawnTimer = 0 + total = total + 1 + local x = math.random(-8, 8) + local ref = __rbxl_spawn_npc("character-b", x, 1, 38, "Враг", 30, 2.5) + local e = { ref = ref, dead = false } + table.insert(enemies, e) + -- Отложим moveTo пока NPC создастся + task.delay(0.3, function() + __rbxl_npc_moveto(ref, 0, 2) + end) + -- Клик по этому NPC → шлём в общий event + __rbxl_npc_on_click(ref, function() + local ev = game:GetService("ReplicatedStorage"):FindFirstChild("EnemyClicked") + if ev then ev:Fire(ref) end + end) + end +end) + +-- Проверка прорыва каждые 0.4с +local leakTimer = 0 +RunService.Heartbeat:Connect(function(dt) + if over then return end + leakTimer = leakTimer + dt + if leakTimer < 0.4 then return end + leakTimer = 0 + for _, e in ipairs(enemies) do + if not e.dead then + local ez = __rbxl_npc_z(e.ref) + local ex = __rbxl_npc_x(e.ref) + -- ez=0 ex=0 пока NPC не зарезолвлен — пропускаем + if not (ex == 0 and ez == 0) and ez < 4 then + e.dead = true + __rbxl_npc_remove(e.ref) + leaked = leaked + 1 + loseSound:Play() + __rbxl_show_text("Враг прорвался! (" .. leaked .. "/" .. MAX_LEAK .. ")", 2) + if leaked >= MAX_LEAK and not over then + over = true + __rbxl_show_text("База разрушена! Поражение.", 5) + end + end + end + end +end)`, + }, + + // ═══════════════════════════════════════════════════════════════ + // ИГРЫ 32-50: явных Lua-версий пока нет. // buildGameProject в docsGamesBuilders.js использует generateFallbackLua // (главный скрипт → показ подсказки + слушает FinishReached → // победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached; diff --git a/src/community/docsLessons.jsx b/src/community/docsLessons.jsx index 80ff32e..bdb1036 100644 --- a/src/community/docsLessons.jsx +++ b/src/community/docsLessons.jsx @@ -4197,7 +4197,7 @@ game.self.onTouch(() => { по заданиям.

- {`// === ИГРА «КВЕСТ С ЗАДАНИЯМИ» — главный скрипт === + {`// === ИГРА «КВЕСТ С ЗАДАНИЯМИ» — главный скрипт === // этап квеста: 0=не начат, 1=собрать монетку, 2=дойти до флага, // 3=вернуться к NPC, 4=готово @@ -4245,7 +4245,7 @@ game.onMessage('flag-done', () => { stage = 3; game.sound.play('pickup'); game.ui.showText('Квест: вернись к квестодателю', 3); -});`} +});`}

Главное здесь — переменная stage: