docs(42) + feat(g43): «Гонка с препятствиями»

g42 docs: CodeBoth 4 скрипта.

g43 паритет:
- Heartbeat: time += dt → timer
- BindableEvents Boost/Spike/FinishReached
- Boost → set_speed(1.8) + pickup + 'УСКОРЕНИЕ!', task.delay 3 → set_speed(1)
- Spike → damage_player(15) + set_speed(0.5) + hit, task.delay 1.5 → 1
- Finish → 'Финиш! Время: X сек' + confetti
- 3 g43_boost_N + 5 g43_spike_N: Touched throttle 1с → Fire соответствующее
- g43_finish: Touched → FinishReached:Fire

Shim: __rbxl_set_speed(mul) → cmd 'player.setSpeed' с полем 'mul'.
This commit is contained in:
min 2026-06-09 22:55:54 +03:00
parent 4085fce0d3
commit 019068cffa
3 changed files with 128 additions and 9 deletions

View File

@ -3943,7 +3943,122 @@ end)`,
}, },
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
// ИГРЫ 43-50: явных Lua-версий пока нет. // ИГРА 43 — «Гонка с препятствиями»
// ═══════════════════════════════════════════════════════════════
'obstacle-race': (function() {
const BOOST_IDS = [1, 2, 3]; // id 1-3 — бусты
const SPIKE_IDS = [4, 5, 6, 7, 8]; // id 4-8 — шипы
const overrides = {
g43_main: `-- === ИГРА «ГОНКА С ПРЕПЯТСТВИЯМИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST}
local RunService = game:GetService("RunService")
local time = 0
local won = false
__rbxl_timer_set(0)
__rbxl_show_text("Гонка! Синее ускоряет, шипы мешают", 4)
local pickupSound = Instance.new("Sound", workspace)
pickupSound.SoundId = "pickup"; pickupSound.Volume = 0.7
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
-- Таймер каждый кадр
RunService.Heartbeat:Connect(function(dt)
if won then return end
time = time + dt
__rbxl_timer_set(time)
end)
-- Буст ускоряет на 3с
local boostEvent = getEvent("Boost")
boostEvent.Event:Connect(function()
__rbxl_set_speed(1.8)
pickupSound:Play()
__rbxl_show_text("УСКОРЕНИЕ!", 1)
task.delay(3, function() __rbxl_set_speed(1) end)
end)
-- Шип бьёт + замедляет на 1.5с
local spikeEvent = getEvent("Spike")
spikeEvent.Event:Connect(function()
__rbxl_damage_player(15)
__rbxl_set_speed(0.5)
hitSound:Play()
task.delay(1.5, function() __rbxl_set_speed(1) end)
end)
-- Финиш
local finishEvent = getEvent("FinishReached")
finishEvent.Event:Connect(function()
if won then return end
won = true
local t = math.floor(time * 10) / 10
__rbxl_show_text("Финиш! Время: " .. t .. " сек", 6)
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)`,
g43_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 → Boost:Fire (throttle 1с)
const boostScript = `-- === Скрипт буста (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local lastFire = 0
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 - lastFire < 1 then return end
lastFire = now
local ev = ReplicatedStorage:FindFirstChild("Boost")
if ev then ev:Fire() end
end)`;
for (const bid of BOOST_IDS) {
overrides['g43_boost_' + bid] = boostScript;
}
// Шипы — Touched → Spike:Fire (throttle 1с)
const spikeScript = `-- === Скрипт шипа-ловушки (Lua) ===
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local part = script.Parent
local lastFire = 0
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 - lastFire < 1 then return end
lastFire = now
local ev = ReplicatedStorage:FindFirstChild("Spike")
if ev then ev:Fire() end
end)`;
for (const sid of SPIKE_IDS) {
overrides['g43_spike_' + sid] = spikeScript;
}
return overrides;
})(),
// ═══════════════════════════════════════════════════════════════
// ИГРЫ 44-50: явных Lua-версий пока нет.
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua // buildGameProject в docsGamesBuilders.js использует generateFallbackLua
// (главный скрипт → показ подсказки + слушает FinishReached → // (главный скрипт → показ подсказки + слушает FinishReached →
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached; // победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;

View File

@ -5986,7 +5986,7 @@ game.self.onTouch(() => {
<h3 className="lessonH">Шаг 2. Главный скрипт</h3> <h3 className="lessonH">Шаг 2. Главный скрипт</h3>
<ScriptKind kind="global" /> <ScriptKind kind="global" />
<Code>{`// === ИГРА «RPG-ДЕРЕВНЯ» — главный скрипт === <CodeBoth game="rpg-village" script="g42_main">{`// === ИГРА «RPG-ДЕРЕВНЯ» — главный скрипт ===
// этап: 0=начало, 1=ищем амулет, 2=несём кузнецу, 3=готово // этап: 0=начало, 1=ищем амулет, 2=несём кузнецу, 3=готово
let stage = 0; let stage = 0;
@ -6038,7 +6038,7 @@ game.onMessage('smithTalk', () => {
} else { } else {
smith.say('Принеси мне амулет — поговори со старостой.', 4); smith.say('Принеси мне амулет — поговори со старостой.', 4);
} }
});`}</Code> });`}</CodeBoth>
<p>Разберём:</p> <p>Разберём:</p>
<ul> <ul>
<li><code>stage</code> переменная-этап: 0 начало, <li><code>stage</code> переменная-этап: 0 начало,
@ -6057,21 +6057,21 @@ game.onMessage('smithTalk', () => {
<h3 className="lessonH">Шаг 3. Скрипты NPC и амулета</h3> <h3 className="lessonH">Шаг 3. Скрипты NPC и амулета</h3>
<ScriptKind kind="object" on="тумбу старосты" /> <ScriptKind kind="object" on="тумбу старосты" />
<Code>{`// === Скрипт старосты === <CodeBoth game="rpg-village" script="g42_elder">{`// === Скрипт старосты ===
game.self.onInteract(() => { game.self.onInteract(() => {
game.broadcast('elderTalk'); game.broadcast('elderTalk');
}, { text: 'Поговорить со старостой', distance: 4 });`}</Code> }, { text: 'Поговорить со старостой', distance: 4 });`}</CodeBoth>
<ScriptKind kind="object" on="тумбу кузнеца" /> <ScriptKind kind="object" on="тумбу кузнеца" />
<Code>{`// === Скрипт кузнеца === <CodeBoth game="rpg-village" script="g42_smith">{`// === Скрипт кузнеца ===
game.self.onInteract(() => { game.self.onInteract(() => {
game.broadcast('smithTalk'); game.broadcast('smithTalk');
}, { text: 'Поговорить с кузнецом', distance: 4 });`}</Code> }, { text: 'Поговорить с кузнецом', distance: 4 });`}</CodeBoth>
<ScriptKind kind="object" on="фиолетовый амулет" /> <ScriptKind kind="object" on="фиолетовый амулет" />
<Code>{`// === Скрипт амулета === <CodeBoth game="rpg-village" script="g42_amulet">{`// === Скрипт амулета ===
game.self.onTouch(() => { game.self.onTouch(() => {
game.broadcast('takeAmulet'); game.broadcast('takeAmulet');
game.self.delete(); game.self.delete();
});`}</Code> });`}</CodeBoth>
<Note> <Note>
Тумба-куб это «кнопка разговора». Сам NPC создаётся Тумба-куб это «кнопка разговора». Сам NPC создаётся
скриптом и стоит рядом с тумбой. Игрок жмёт скриптом и стоит рядом с тумбой. Игрок жмёт

View File

@ -2000,6 +2000,10 @@ export function registerRobloxShim(lua, opts) {
global.set('__rbxl_set_spawn', (x, y, z) => { global.set('__rbxl_set_spawn', (x, y, z) => {
send('player.setSpawn', { x: +x || 0, y: +y || 1, z: +z || 0 }); send('player.setSpawn', { x: +x || 0, y: +y || 1, z: +z || 0 });
}); });
// Множитель скорости — паритет с JS game.player.setSpeed(mul). 1=обычная.
global.set('__rbxl_set_speed', (mul) => {
send('player.setSpeed', { mul: +mul || 1 });
});
// Камера-облёт — паритет с 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 неудобен.