feat: 50 игр на Lua + импорт Roblox для всех + поддержка Lua в плеере #39
@ -4228,7 +4228,122 @@ end)`;
|
|||||||
})(),
|
})(),
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
// ИГРЫ 45-50: явных Lua-версий пока нет.
|
// ИГРА 45 — «Стрелялка-арена»
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
'arena-shooter': {
|
||||||
|
g45_main: `-- === ИГРА «СТРЕЛЯЛКА-АРЕНА» — главный скрипт (Lua) ===
|
||||||
|
${SNIPPET_BROADCAST}
|
||||||
|
|
||||||
|
local Players = game:GetService("Players")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
local player = Players.LocalPlayer
|
||||||
|
|
||||||
|
local GOAL = 15
|
||||||
|
local score = 0
|
||||||
|
local over = false
|
||||||
|
|
||||||
|
-- Враги: { ref → { ref, alive, lastDmg } }
|
||||||
|
local enemies = {}
|
||||||
|
|
||||||
|
__rbxl_score_set(0)
|
||||||
|
__rbxl_show_text("Перебей 15 врагов! Кликай по ним", 3)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Подписка на смерть игрока
|
||||||
|
task.delay(0.5, function()
|
||||||
|
local char = player.Character or player.CharacterAdded:Wait()
|
||||||
|
local h = char:WaitForChild("Humanoid", 2)
|
||||||
|
if h then
|
||||||
|
h.Died:Connect(function()
|
||||||
|
if over then return end
|
||||||
|
over = true
|
||||||
|
__rbxl_show_text("Поражение! Тебя одолели враги.", 5)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Клик по врагу: EnemyClicked:Fire(ref)
|
||||||
|
local clickEvent = getEvent("EnemyClicked")
|
||||||
|
clickEvent.Event:Connect(function(localRef)
|
||||||
|
if over then return end
|
||||||
|
local e = enemies[localRef]
|
||||||
|
if not e or not e.alive then return end
|
||||||
|
local px = __rbxl_player_x()
|
||||||
|
local pz = __rbxl_player_z()
|
||||||
|
local ex = __rbxl_npc_x(localRef)
|
||||||
|
local ez = __rbxl_npc_z(localRef)
|
||||||
|
if ex == 0 and ez == 0 then return end
|
||||||
|
local dx = px - ex
|
||||||
|
local dz = pz - ez
|
||||||
|
local dist = math.sqrt(dx*dx + dz*dz)
|
||||||
|
if dist < 6 then
|
||||||
|
e.alive = false
|
||||||
|
__rbxl_npc_remove(localRef)
|
||||||
|
__rbxl_spawn_particles("explosion", ex, 2, ez, 0.4, 1)
|
||||||
|
hitSound:Play()
|
||||||
|
score = score + 1
|
||||||
|
__rbxl_score_set(score)
|
||||||
|
if score >= GOAL and not over then
|
||||||
|
over = true
|
||||||
|
__rbxl_show_text("Победа! Арена зачищена!", 5)
|
||||||
|
winSound:Play()
|
||||||
|
__rbxl_spawn_particles("confetti", px, 3, pz, 3, 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Спавн каждые 1.8с
|
||||||
|
local spawnTimer = 0
|
||||||
|
RunService.Heartbeat:Connect(function(dt)
|
||||||
|
if over or score >= GOAL then return end
|
||||||
|
spawnTimer = spawnTimer + dt
|
||||||
|
if spawnTimer < 1.8 then return end
|
||||||
|
spawnTimer = 0
|
||||||
|
local angle = math.random() * math.pi * 2
|
||||||
|
local ex = math.cos(angle) * 11
|
||||||
|
local ez = math.sin(angle) * 11
|
||||||
|
local ref = __rbxl_spawn_npc("character-b", ex, 1, ez, "Враг", 30, 2.2)
|
||||||
|
enemies[ref] = { ref = ref, alive = true, lastDmg = 0 }
|
||||||
|
task.delay(0.3, function()
|
||||||
|
__rbxl_npc_follow(ref, "player")
|
||||||
|
end)
|
||||||
|
__rbxl_npc_on_click(ref, function()
|
||||||
|
local ev = game:GetService("ReplicatedStorage"):FindFirstChild("EnemyClicked")
|
||||||
|
if ev then ev:Fire(ref) end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Враги бьют игрока вблизи (каждые 0.7с)
|
||||||
|
RunService.Heartbeat:Connect(function()
|
||||||
|
if over then return end
|
||||||
|
local px = __rbxl_player_x()
|
||||||
|
local pz = __rbxl_player_z()
|
||||||
|
local now = tick()
|
||||||
|
for _, e in pairs(enemies) do
|
||||||
|
if e.alive then
|
||||||
|
local ex = __rbxl_npc_x(e.ref)
|
||||||
|
local ez = __rbxl_npc_z(e.ref)
|
||||||
|
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.8 and now - e.lastDmg > 0.7 then
|
||||||
|
e.lastDmg = now
|
||||||
|
__rbxl_damage_player(10)
|
||||||
|
hitSound:Play()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
// ИГРЫ 46-50: явных Lua-версий пока нет.
|
||||||
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua
|
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua
|
||||||
// (главный скрипт → показ подсказки + слушает FinishReached →
|
// (главный скрипт → показ подсказки + слушает FinishReached →
|
||||||
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;
|
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;
|
||||||
|
|||||||
@ -6291,7 +6291,7 @@ game.self.onTouch(() => {
|
|||||||
стрелять и проверяет, кто победил.
|
стрелять и проверяет, кто победил.
|
||||||
</p>
|
</p>
|
||||||
<ScriptKind kind="global" />
|
<ScriptKind kind="global" />
|
||||||
<Code>{`// === ИГРА «TOWER DEFENSE» — главный скрипт ===
|
<CodeBoth game="tower-defense" script="g44_main">{`// === ИГРА «TOWER DEFENSE» — главный скрипт ===
|
||||||
|
|
||||||
let leaked = 0; // врагов прошло до базы
|
let leaked = 0; // врагов прошло до базы
|
||||||
const MAX_LEAK = 8;
|
const MAX_LEAK = 8;
|
||||||
@ -6374,7 +6374,7 @@ game.every(0.5, () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});`}</Code>
|
});`}</CodeBoth>
|
||||||
<p>Разберём:</p>
|
<p>Разберём:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>towers</code> и <code>enemies</code> — два списка:
|
<li><code>towers</code> и <code>enemies</code> — два списка:
|
||||||
@ -6395,7 +6395,7 @@ game.every(0.5, () => {
|
|||||||
|
|
||||||
<h3 className="lessonH">Шаг 3. Скрипт площадки под башню</h3>
|
<h3 className="lessonH">Шаг 3. Скрипт площадки под башню</h3>
|
||||||
<ScriptKind kind="object" on="каждую площадку" />
|
<ScriptKind kind="object" on="каждую площадку" />
|
||||||
<Code>{`// === Скрипт площадки под башню ===
|
<CodeBoth game="tower-defense" script="g44_slot_1">{`// === Скрипт площадки под башню ===
|
||||||
let built = false;
|
let built = false;
|
||||||
game.self.onInteract(() => {
|
game.self.onInteract(() => {
|
||||||
if (built) return;
|
if (built) return;
|
||||||
@ -6408,7 +6408,7 @@ game.self.onInteract(() => {
|
|||||||
color: '#ffcc33',
|
color: '#ffcc33',
|
||||||
});
|
});
|
||||||
game.broadcast('addTower', { x: pos.x, z: pos.z });
|
game.broadcast('addTower', { x: pos.x, z: pos.z });
|
||||||
}, { text: 'Построить башню', distance: 4 });`}</Code>
|
}, { text: 'Построить башню', distance: 4 });`}</CodeBoth>
|
||||||
<p>
|
<p>
|
||||||
При нажатии <kbd className="kbd">E</kbd> скрипт создаёт
|
При нажатии <kbd className="kbd">E</kbd> скрипт создаёт
|
||||||
жёлтый цилиндр-башню над площадкой и шлёт сообщение
|
жёлтый цилиндр-башню над площадкой и шлёт сообщение
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user