docs(36) + feat(g37): «Полоса препятствий»

g36 docs: CodeBoth main+box_1.

g37 паритет:
- task.delay 0.2 ДвижПлатформа yoyo loop (x: -0.5 ↔ 3, 2с)
- Heartbeat: py<-3 → LoadCharacter + lose
- BindableEvents CheckpointReached/FinishReached
- 6 g37_spike_N: Touched → damage(25) + hit sound (i-frames 0.5с)
- g37_cp: Touched → CheckpointReached:Fire → setSpawn(-0.5,1,24)
- g37_finish: Touched → FinishReached:Fire → win + confetti

Shim: __rbxl_set_spawn(x,y,z).
This commit is contained in:
min 2026-06-09 22:32:55 +03:00
parent b0bdfb6e29
commit f0025f0dad
3 changed files with 133 additions and 5 deletions

View File

@ -3278,7 +3278,131 @@ end)`;
})(), })(),
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
// ИГРЫ 37-50: явных Lua-версий пока нет. // ИГРА 37 — «Полоса препятствий»
// ═══════════════════════════════════════════════════════════════
'obstacle-course': (function() {
const SPIKE_IDS = [1, 2, 3, 4, 5, 6]; // id 1-6 — шипы
const overrides = {
g37_main: `-- === ИГРА «ПОЛОСА ПРЕПЯТСТВИЙ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local player = Players.LocalPlayer
local won = false
__rbxl_show_text("Пройди полосу: шипы, ямы, платформа!", 4)
local loseSound = Instance.new("Sound", workspace)
loseSound.SoundId = "lose"; loseSound.Volume = 0.7
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
-- Движущаяся платформа: tween yoyo x: -0.5 3, 2с
task.delay(0.2, function()
local mover = workspace:FindFirstChild("ДвижПлатформа")
if mover then
local mp = mover.Position
local startX = mp.X
local function loopMove()
local g1 = { Position = Vector3.new(3, mp.Y, mp.Z) }
local t1 = TweenService:Create(mover, TweenInfo.new(2), g1)
t1:Play()
t1.Completed:Connect(function()
local g2 = { Position = Vector3.new(startX, mp.Y, mp.Z) }
local t2 = TweenService:Create(mover, TweenInfo.new(2), g2)
t2:Play()
t2.Completed:Connect(loopMove)
end)
end
loopMove()
end
end)
-- Респаун при падении
RunService.Heartbeat:Connect(function()
if won then return end
local py = __rbxl_player_y()
if py < -3 then
player:LoadCharacter()
loseSound:Play()
end
end)
-- Чекпоинт обновляем точку возрождения
local cpEvent = getEvent("CheckpointReached")
cpEvent.Event:Connect(function()
__rbxl_set_spawn(-0.5, 1, 24)
__rbxl_show_text("Чекпоинт сохранён!", 2)
pickupSound:Play()
end)
-- Финиш
local winEvent = getEvent("FinishReached")
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)`,
g37_cp: `-- === Скрипт чекпоинта (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("CheckpointReached")
if ev then ev:Fire() end
end)`,
g37_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)`,
};
// Шипы — Touched → damage 25 + hit
const spikeScript = `-- === Скрипт шипа (Lua) ===
local part = script.Parent
local lastHit = 0
local hitSound = Instance.new("Sound", part)
hitSound.SoundId = "hit"; hitSound.Volume = 0.6
part.Touched:Connect(function(hit)
local h = hit.Parent and hit.Parent:FindFirstChild("Humanoid")
if not h then return end
local now = tick()
if now - lastHit < 0.5 then return end -- i-frames
lastHit = now
__rbxl_damage_player(25)
hitSound:Play()
end)`;
for (const sid of SPIKE_IDS) {
overrides['g37_spike_' + sid] = spikeScript;
}
return overrides;
})(),
// ═══════════════════════════════════════════════════════════════
// ИГРЫ 38-50: явных Lua-версий пока нет.
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua // buildGameProject в docsGamesBuilders.js использует generateFallbackLua
// (главный скрипт → показ подсказки + слушает FinishReached → // (главный скрипт → показ подсказки + слушает FinishReached →
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached; // победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;

View File

@ -5095,7 +5095,7 @@ game.onTick((dt) => {
момент победы. момент победы.
</p> </p>
<ScriptKind kind="global" /> <ScriptKind kind="global" />
<Code>{`// === ИГРА «ГОЛОВОЛОМКА С ЯЩИКАМИ» — главный скрипт === <CodeBoth game="box-puzzle" script="g36_main">{`// === ИГРА «ГОЛОВОЛОМКА С ЯЩИКАМИ» — главный скрипт ===
// для каждого ящика на какой плите он сейчас (true/false) // для каждого ящика на какой плите он сейчас (true/false)
const onPlate = [false, false, false]; const onPlate = [false, false, false];
@ -5119,7 +5119,7 @@ game.onMessage('box', (d) => {
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> <p>Разберём:</p>
<ul> <ul>
<li><code>onPlate</code> массив из трёх «галочек»: стоит <li><code>onPlate</code> массив из трёх «галочек»: стоит
@ -5138,7 +5138,7 @@ game.onMessage('box', (d) => {
поменяй число <code>i</code> в сообщении на 1 и 2). поменяй число <code>i</code> в сообщении на 1 и 2).
</p> </p>
<ScriptKind kind="object" on="каждый ящик" /> <ScriptKind kind="object" on="каждый ящик" />
<Code>{`// === Скрипт ящика 1 === <CodeBoth game="box-puzzle" script="g36_box_1">{`// === Скрипт ящика 1 ===
// ряд позиций по Z, по которым прыгает ящик // ряд позиций по Z, по которым прыгает ящик
const ROW = [-6, -3, 0, 3, 6]; const ROW = [-6, -3, 0, 3, 6];
@ -5152,7 +5152,7 @@ game.self.onInteract(() => {
game.tween(game.self.ref, { z: z }, { duration: 0.4, easing: 'ease' }); game.tween(game.self.ref, { z: z }, { duration: 0.4, easing: 'ease' });
// сообщаем главному скрипту стоит ли ящик на плите // сообщаем главному скрипту стоит ли ящик на плите
game.broadcast('box', { i: 0, on: z === PLATE_Z }); game.broadcast('box', { i: 0, on: z === PLATE_Z });
}, { text: 'Двинуть ящик', distance: 3 });`}</Code> }, { text: 'Двинуть ящик', distance: 3 });`}</CodeBoth>
<p>Что происходит:</p> <p>Что происходит:</p>
<ul> <ul>
<li><code>ROW</code> пять клеток ряда (значения Z);</li> <li><code>ROW</code> пять клеток ряда (значения Z);</li>

View File

@ -1996,6 +1996,10 @@ export function registerRobloxShim(lua, opts) {
global.set('__rbxl_set_double_jump', (enabled) => { global.set('__rbxl_set_double_jump', (enabled) => {
send('player.setDoubleJump', { enabled: !!enabled }); send('player.setDoubleJump', { enabled: !!enabled });
}); });
// Точка возрождения — паритет с JS game.player.setSpawn({x,y,z}).
global.set('__rbxl_set_spawn', (x, y, z) => {
send('player.setSpawn', { x: +x || 0, y: +y || 1, z: +z || 0 });
});
// Камера-облёт — паритет с JS game.camera.cutscene(points, opts). // Камера-облёт — паритет с JS game.camera.cutscene(points, opts).
// pointsFlat/lookAtFlat: x1,y1,z1,x2,y2,z2,... — потому что массив // pointsFlat/lookAtFlat: x1,y1,z1,x2,y2,z2,... — потому что массив
// объектов в wasmoon через C-boundary неудобен. // объектов в wasmoon через C-boundary неудобен.