feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39

Merged
min merged 215 commits from feat/lua-50-games-bundle into main 2026-06-09 21:59:25 +00:00
2 changed files with 71 additions and 5 deletions
Showing only changes of commit 92a9ef220d - Show all commits

View File

@ -3125,7 +3125,73 @@ end)`;
})(), })(),
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
// ИГРЫ 35-50: явных Lua-версий пока нет. // ИГРА 35 — «Прятки от NPC»
// ═══════════════════════════════════════════════════════════════
'hide-from-npc': {
g35_main: `-- === ИГРА «ПРЯТКИ ОТ NPC» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local SURVIVE = 40
local time = 0
local won = false
local lastCaughtTime = 0
__rbxl_timer_set(0)
__rbxl_show_text("Прячься за стенами 40 секунд!", 4)
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-искатель ходит за игроком
local seekerRef = __rbxl_spawn_npc("character-b", 0, 1, 10, "Искатель", 100, 3)
task.delay(0.3, function()
__rbxl_npc_follow(seekerRef, "player")
end)
RunService.Heartbeat:Connect(function(dt)
if won then return end
time = time + dt
__rbxl_timer_set(time)
-- Поймал респаун
local px = __rbxl_player_x()
local pz = __rbxl_player_z()
local ex = __rbxl_npc_x(seekerRef)
local ez = __rbxl_npc_z(seekerRef)
if not (ex == 0 and ez == 0) then
local dx = px - ex
local dz = pz - ez
local dist = math.sqrt(dx*dx + dz*dz)
if dist < 1.7 then
local now = tick()
if now - lastCaughtTime > 2 then
lastCaughtTime = now
player:LoadCharacter()
__rbxl_show_text("Найден! Прячься снова!", 1.5)
loseSound:Play()
end
end
end
-- Продержался 40 секунд победа
if time >= SURVIVE then
won = true
__rbxl_npc_stop(seekerRef)
__rbxl_show_text("Победа! Ты прятался 40 секунд!", 5)
winSound:Play()
__rbxl_spawn_particles("confetti", px, 1, pz, 3, 3)
end
end)`,
},
// ═══════════════════════════════════════════════════════════════
// ИГРЫ 36-50: явных Lua-версий пока нет.
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua // buildGameProject в docsGamesBuilders.js использует generateFallbackLua
// (главный скрипт → показ подсказки + слушает FinishReached → // (главный скрипт → показ подсказки + слушает FinishReached →
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached; // победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;

View File

@ -4811,7 +4811,7 @@ game.onTick(() => {
<h3 className="lessonH">Шаг 2. Главный скрипт</h3> <h3 className="lessonH">Шаг 2. Главный скрипт</h3>
<ScriptKind kind="global" /> <ScriptKind kind="global" />
<Code>{`// === ИГРА «СБОР УРОЖАЯ» — главный скрипт === <CodeBoth game="harvest" script="g34_main">{`// === ИГРА «СБОР УРОЖАЯ» — главный скрипт ===
let harvested = 0; let harvested = 0;
const GOAL = 6; const GOAL = 6;
@ -4831,7 +4831,7 @@ game.onMessage('harvested', () => {
game.scene.spawnParticles('confetti', game.scene.spawnParticles('confetti',
{ x: p.x, y: p.y + 3, z: p.z }, { duration: 3, count: 3 }); { x: p.x, y: p.y + 3, z: p.z }, { duration: 3, count: 3 });
} }
});`}</Code> });`}</CodeBoth>
<p> <p>
Главный скрипт считает собранные растения и проверяет Главный скрипт считает собранные растения и проверяет
победу. Каждое растение работает в своей «песочнице», победу. Каждое растение работает в своей «песочнице»,
@ -4845,7 +4845,7 @@ game.onMessage('harvested', () => {
<h3 className="lessonH">Шаг 3. Скрипт растения</h3> <h3 className="lessonH">Шаг 3. Скрипт растения</h3>
<p>Этот скрипт вешается на <b>каждое</b> растение.</p> <p>Этот скрипт вешается на <b>каждое</b> растение.</p>
<ScriptKind kind="object" on="каждое растение" /> <ScriptKind kind="object" on="каждое растение" />
<Code>{`// === Скрипт растения === <CodeBoth game="harvest" script="g34_plant_1">{`// === Скрипт растения ===
let ripe = false; // растение выросло (спелое)? let ripe = false; // растение выросло (спелое)?
let picked = false; let picked = false;
@ -4870,7 +4870,7 @@ game.self.onInteract(() => {
picked = true; picked = true;
game.self.delete(); game.self.delete();
game.broadcast('harvested'); game.broadcast('harvested');
}, { text: 'Собрать', distance: 3 });`}</Code> }, { text: 'Собрать', distance: 3 });`}</CodeBoth>
<p>Разберём:</p> <p>Разберём:</p>
<ul> <ul>
<li><code>game.tween(ref, {'{ sx, sy, sz, y }'}, опции)</code> <li><code>game.tween(ref, {'{ sx, sy, sz, y }'}, опции)</code>