docs(44) + feat(g45): «Стрелялка-арена»

g44 docs: CodeBoth main+slot_1.

g45 паритет:
- GOAL=15, score/over
- Humanoid.Died → 'Поражение!'
- BindableEvent EnemyClicked(ref)
- Heartbeat spawn 1.8с: радиус=11 cos/sin → npc 'character-b'
  hp=30 speed=2.2 follow('player')
- npc_on_click → EnemyClicked:Fire(ref)
- Главный: dist<6 → npc.remove + explosion + score++
- 15 → 'Победа!' + confetti
- Heartbeat damage: каждый враг dist<1.8 + last>0.7 → damage_player(10) + hit
This commit is contained in:
min 2026-06-09 23:04:21 +03:00
parent dc669a51f4
commit 1c5e5fe5bb
2 changed files with 120 additions and 5 deletions

View File

@ -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
// (главный скрипт → показ подсказки + слушает FinishReached →
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;

View File

@ -6291,7 +6291,7 @@ game.self.onTouch(() => {
стрелять и проверяет, кто победил.
</p>
<ScriptKind kind="global" />
<Code>{`// === ИГРА «TOWER DEFENSE» — главный скрипт ===
<CodeBoth game="tower-defense" script="g44_main">{`// === ИГРА «TOWER DEFENSE» — главный скрипт ===
let leaked = 0; // врагов прошло до базы
const MAX_LEAK = 8;
@ -6374,7 +6374,7 @@ game.every(0.5, () => {
}
}
}
});`}</Code>
});`}</CodeBoth>
<p>Разберём:</p>
<ul>
<li><code>towers</code> и <code>enemies</code> два списка:
@ -6395,7 +6395,7 @@ game.every(0.5, () => {
<h3 className="lessonH">Шаг 3. Скрипт площадки под башню</h3>
<ScriptKind kind="object" on="каждую площадку" />
<Code>{`// === Скрипт площадки под башню ===
<CodeBoth game="tower-defense" script="g44_slot_1">{`// === Скрипт площадки под башню ===
let built = false;
game.self.onInteract(() => {
if (built) return;
@ -6408,7 +6408,7 @@ game.self.onInteract(() => {
color: '#ffcc33',
});
game.broadcast('addTower', { x: pos.x, z: pos.z });
}, { text: 'Построить башню', distance: 4 });`}</Code>
}, { text: 'Построить башню', distance: 4 });`}</CodeBoth>
<p>
При нажатии <kbd className="kbd">E</kbd> скрипт создаёт
жёлтый цилиндр-башню над площадкой и шлёт сообщение