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 126 additions and 23 deletions
Showing only changes of commit f69df55e3b - Show all commits

View File

@ -2607,26 +2607,129 @@ end)`;
// ИГРА 30 — «Квесты» // ИГРА 30 — «Квесты»
// ═══════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════
'quest-tasks': { 'quest-tasks': {
g30_main: `-- === ИГРА «КВЕСТЫ» (Lua) === g30_main: `-- === ИГРА «КВЕСТ С ЗАДАНИЯМИ» — главный скрипт (Lua) ===
${SNIPPET_BROADCAST} ${SNIPPET_BROADCAST}
local quests = { local stage = 0 -- 0=не начат, 1=собрать монетку, 2=дойти до флага, 3=вернуться, 4=готово
{ name = "Собери 5 ягод", goal = 5, current = 0 },
{ name = "Победи врага", goal = 1, current = 0 },
{ name = "Дойди до башни", goal = 1, current = 0 },
}
print("Квесты:") local function setObjective(text, color)
for i, q in ipairs(quests) do print(" " .. i .. ". " .. q.name) end __rbxl_hud_set("objective", "ЦЕЛЬ: " .. text, 50, 8, color or "#ffe066", 24)
end
setObjective("подойди к квестодателю и нажми E")
local ev = getEvent("QuestProgress") -- Спавним NPC рядом с тумбой (NPC = квестодатель)
ev.Event:Connect(function(idx, amount) local npcRef = __rbxl_spawn_npc("character-a", 1.5, 1, 2, "Старейшина", 100, 0)
quests[idx].current = quests[idx].current + (amount or 1)
local q = quests[idx] local coinSound = Instance.new("Sound", workspace)
print(q.name .. ": " .. q.current .. "/" .. q.goal) coinSound.SoundId = "coin"; coinSound.Volume = 0.7
if q.current >= q.goal then local pickupSound = Instance.new("Sound", workspace)
print("Квест выполнен: " .. q.name) pickupSound.SoundId = "pickup"; pickupSound.Volume = 0.7
local winSound = Instance.new("Sound", workspace)
winSound.SoundId = "win"; winSound.Volume = 1
-- Поговорить с NPC
local talkEvent = getEvent("Talk")
talkEvent.Event:Connect(function()
if stage == 0 then
stage = 1
__rbxl_npc_say(npcRef, "Задание 1: найди жёлтую монетку!", 4)
setObjective("собери жёлтую монетку (слева)")
elseif stage == 3 then
stage = 4
__rbxl_npc_say(npcRef, "Молодец! Квест выполнен!", 4)
__rbxl_hud_set("objective", "КВЕСТ ПРОЙДЕН!", 50, 8, "#22dd55", 26)
__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)
elseif stage == 4 then
__rbxl_npc_say(npcRef, "Спасибо, герой!", 3)
elseif stage == 1 then
__rbxl_npc_say(npcRef, "Ты ещё не собрал монетку!", 3)
elseif stage == 2 then
__rbxl_npc_say(npcRef, "Сначала дойди до синего флага!", 3)
end end
end)
-- Монетка собрана
local coinEvent = getEvent("CoinDone")
coinEvent.Event:Connect(function()
if stage ~= 1 then return end
stage = 2
coinSound:Play()
__rbxl_npc_say(npcRef, "Отлично! Теперь дойди до синего флага.", 4)
__rbxl_show_text("Монетка собрана!", 2)
setObjective("дойди до синего флага (справа)")
end)
-- Флаг достигнут
local flagEvent = getEvent("FlagDone")
flagEvent.Event:Connect(function()
if stage ~= 2 then return end
stage = 3
pickupSound:Play()
__rbxl_show_text("Флаг найден!", 2)
setObjective("вернись к квестодателю и нажми E")
end)`,
g30_npc: `-- === Скрипт квестодателя (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 <= 4
if near ~= hintVisible then
hintVisible = near
if near then
__rbxl_hud_set("g30_npc_hint", "[E] Поговорить", 50, 75, "#ffe44a", 20)
else
__rbxl_hud_set("g30_npc_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("Talk")
if ev then ev:Fire() end
end)`,
g30_coin: `-- === Скрипт квест-монетки (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("CoinDone")
if ev then ev:Fire() end
part:Destroy()
end)`,
g30_flag: `-- === Скрипт квест-флага (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("FlagDone")
if ev then ev:Fire() end
end)`, end)`,
}, },

View File

@ -4019,7 +4019,7 @@ game.self.onTouch(() => {
продаёт ключ, открывает дверь. продаёт ключ, открывает дверь.
</p> </p>
<ScriptKind kind="global" /> <ScriptKind kind="global" />
<Code>{`// === ИГРА «МАГАЗИН» — главный скрипт === <CodeBoth game="shop" script="g29_main">{`// === ИГРА «МАГАЗИН» — главный скрипт ===
let coins = 0; let coins = 0;
const PRICE = 5; // ключ стоит 5 монет const PRICE = 5; // ключ стоит 5 монет
@ -4070,7 +4070,7 @@ game.onMessage('win', () => {
const p = game.player.position; const p = game.player.position;
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>
Каждый объект магазина монетка, прилавок, дверь, финиш Каждый объект магазина монетка, прилавок, дверь, финиш
работает в своей «песочнице» и не видит переменные работает в своей «песочнице» и не видит переменные
@ -4100,23 +4100,23 @@ game.onMessage('win', () => {
<h3 className="lessonH">Шаг 3. Скрипт монетки</h3> <h3 className="lessonH">Шаг 3. Скрипт монетки</h3>
<ScriptKind kind="object" on="каждую монетку" /> <ScriptKind kind="object" on="каждую монетку" />
<Code>{`// === Скрипт монетки === <CodeBoth game="shop" script="g29_coin_1">{`// === Скрипт монетки ===
game.self.onTouch(() => { game.self.onTouch(() => {
game.broadcast('coin'); game.broadcast('coin');
game.self.delete(); game.self.delete();
});`}</Code> });`}</CodeBoth>
<h3 className="lessonH">Шаг 4. Скрипт прилавка и двери</h3> <h3 className="lessonH">Шаг 4. Скрипт прилавка и двери</h3>
<ScriptKind kind="object" on="прилавок" /> <ScriptKind kind="object" on="прилавок" />
<Code>{`// === Скрипт прилавка === <CodeBoth game="shop" script="g29_shop">{`// === Скрипт прилавка ===
game.self.onInteract(() => { game.self.onInteract(() => {
game.broadcast('buy'); game.broadcast('buy');
}, { text: 'Купить ключ (5 монет)', distance: 4 });`}</Code> }, { text: 'Купить ключ (5 монет)', distance: 4 });`}</CodeBoth>
<ScriptKind kind="object" on="дверь" /> <ScriptKind kind="object" on="дверь" />
<Code>{`// === Скрипт двери === <CodeBoth game="shop" script="g29_door">{`// === Скрипт двери ===
game.self.onInteract(() => { game.self.onInteract(() => {
game.broadcast('open-door'); game.broadcast('open-door');
}, { text: 'Открыть дверь', distance: 4 });`}</Code> }, { text: 'Открыть дверь', distance: 4 });`}</CodeBoth>
<p> <p>
И прилавок, и дверь это объекты с взаимодействием по И прилавок, и дверь это объекты с взаимодействием по
<kbd className="kbd">E</kbd>. Подошёл к прилавку и нажал <kbd className="kbd">E</kbd>. Подошёл к прилавку и нажал