docs(43) + feat(g44): «Tower Defense»
g43 docs: CodeBoth main+boost_1+spike_4+finish.
g44 паритет:
- GOAL=14, MAX_LEAK=8, killed/leaked counters
- towers[] и enemies{} (ref-keyed)
- BindableEvent TowerBuilt(x, z)
- Heartbeat spawn: 2.2с → npc 'character-b' speed=2 hp=50 в (-0.5,1,-3),
task.delay 0.3 moveTo (-0.5, 42), on_death → killed++
- Heartbeat fire: 0.8с → каждая башня бьёт ближайшего врага в r=7
(npc.damage 25 + sparks)
- Heartbeat leak: 0.5с → ez>40 → npc.remove + leaked++ + lose
- 4 g44_slot_N: Heartbeat distance(4) + '[E] Построить башню'
E → Instance.new('Part') Cylinder жёлтый + TowerBuilt:Fire
This commit is contained in:
parent
019068cffa
commit
dc669a51f4
@ -4058,7 +4058,177 @@ end)`;
|
|||||||
})(),
|
})(),
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════
|
||||||
// ИГРЫ 44-50: явных Lua-версий пока нет.
|
// ИГРА 44 — «Tower Defense»
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
'tower-defense': (function() {
|
||||||
|
const SLOT_IDS = [1, 2, 3, 4];
|
||||||
|
const overrides = {
|
||||||
|
g44_main: `-- === ИГРА «TOWER DEFENSE» — главный скрипт (Lua) ===
|
||||||
|
${SNIPPET_BROADCAST}
|
||||||
|
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
local MAX_LEAK = 8
|
||||||
|
local GOAL = 14
|
||||||
|
local leaked = 0
|
||||||
|
local killed = 0
|
||||||
|
local over = false
|
||||||
|
|
||||||
|
-- Список башен ({x, z})
|
||||||
|
local towers = {}
|
||||||
|
-- Все враги: ref → { ref, alive }
|
||||||
|
local enemies = {}
|
||||||
|
|
||||||
|
__rbxl_score_set(0)
|
||||||
|
__rbxl_show_text("Ставь башни (E)! Не пропусти врагов", 4)
|
||||||
|
|
||||||
|
local clickSound = Instance.new("Sound", workspace)
|
||||||
|
clickSound.SoundId = "click"; clickSound.Volume = 0.6
|
||||||
|
local loseSound = Instance.new("Sound", workspace)
|
||||||
|
loseSound.SoundId = "lose"; loseSound.Volume = 0.7
|
||||||
|
local winSound = Instance.new("Sound", workspace)
|
||||||
|
winSound.SoundId = "win"; winSound.Volume = 1
|
||||||
|
|
||||||
|
-- Площадки шлют TowerBuilt:Fire(x, z)
|
||||||
|
local towerEvent = getEvent("TowerBuilt")
|
||||||
|
towerEvent.Event:Connect(function(x, z)
|
||||||
|
table.insert(towers, { x = x, z = z })
|
||||||
|
clickSound:Play()
|
||||||
|
__rbxl_show_text("Башня построена!", 1.5)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Спавн врагов каждые 2.2с
|
||||||
|
local total = 0
|
||||||
|
local spawnTimer = 0
|
||||||
|
RunService.Heartbeat:Connect(function(dt)
|
||||||
|
if over then return end
|
||||||
|
if total >= GOAL + MAX_LEAK then return end
|
||||||
|
spawnTimer = spawnTimer + dt
|
||||||
|
if spawnTimer < 2.2 then return end
|
||||||
|
spawnTimer = 0
|
||||||
|
total = total + 1
|
||||||
|
local ref = __rbxl_spawn_npc("character-b", -0.5, 1, -3, "Враг", 50, 2)
|
||||||
|
local rec = { ref = ref, alive = true }
|
||||||
|
enemies[ref] = rec
|
||||||
|
task.delay(0.3, function()
|
||||||
|
__rbxl_npc_moveto(ref, -0.5, 42)
|
||||||
|
end)
|
||||||
|
__rbxl_npc_on_death(ref, function()
|
||||||
|
rec.alive = false
|
||||||
|
killed = killed + 1
|
||||||
|
__rbxl_score_set(killed)
|
||||||
|
if killed >= GOAL and not over then
|
||||||
|
over = true
|
||||||
|
__rbxl_show_text("Победа! База защищена!", 5)
|
||||||
|
winSound:Play()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Башни стреляют каждые 0.8с — бьют врага в радиусе 7 от любой башни
|
||||||
|
local fireTimer = 0
|
||||||
|
RunService.Heartbeat:Connect(function(dt)
|
||||||
|
if over then return end
|
||||||
|
fireTimer = fireTimer + dt
|
||||||
|
if fireTimer < 0.8 then return end
|
||||||
|
fireTimer = 0
|
||||||
|
for _, t in ipairs(towers) do
|
||||||
|
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 = t.x - ex
|
||||||
|
local dz = t.z - ez
|
||||||
|
local dist = math.sqrt(dx*dx + dz*dz)
|
||||||
|
if dist < 7 then
|
||||||
|
__rbxl_npc_damage(e.ref, 25)
|
||||||
|
__rbxl_spawn_particles("sparks", ex, 2, ez, 0.3, 1)
|
||||||
|
break -- одна башня — один выстрел за тик
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Прорыв врагов до базы (z > 40) каждые 0.5с
|
||||||
|
local leakTimer = 0
|
||||||
|
RunService.Heartbeat:Connect(function(dt)
|
||||||
|
if over then return end
|
||||||
|
leakTimer = leakTimer + dt
|
||||||
|
if leakTimer < 0.5 then return end
|
||||||
|
leakTimer = 0
|
||||||
|
for _, e in pairs(enemies) do
|
||||||
|
if e.alive then
|
||||||
|
local ez = __rbxl_npc_z(e.ref)
|
||||||
|
local ex = __rbxl_npc_x(e.ref)
|
||||||
|
if not (ex == 0 and ez == 0) and ez > 40 then
|
||||||
|
e.alive = false
|
||||||
|
__rbxl_npc_remove(e.ref)
|
||||||
|
leaked = leaked + 1
|
||||||
|
loseSound:Play()
|
||||||
|
__rbxl_show_text("Враг прорвался! (" .. leaked .. "/" .. MAX_LEAK .. ")", 2)
|
||||||
|
if leaked >= MAX_LEAK and not over then
|
||||||
|
over = true
|
||||||
|
__rbxl_show_text("База разрушена! Поражение.", 5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)`,
|
||||||
|
};
|
||||||
|
// 4 площадки — E ставит жёлтый цилиндр-башню сверху
|
||||||
|
for (const sid of SLOT_IDS) {
|
||||||
|
overrides['g44_slot_' + sid] = `-- === Скрипт площадки под башню (Lua) ===
|
||||||
|
local UserInputService = game:GetService("UserInputService")
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
local part = script.Parent
|
||||||
|
local built = false
|
||||||
|
local hintVisible = false
|
||||||
|
|
||||||
|
RunService.Heartbeat:Connect(function()
|
||||||
|
if built then return end
|
||||||
|
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("g44_slot_${sid}_hint", "[E] Построить башню", 50, 75, "#ffe44a", 20)
|
||||||
|
else
|
||||||
|
__rbxl_hud_set("g44_slot_${sid}_hint", nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
UserInputService.InputBegan:Connect(function(input, gp)
|
||||||
|
if gp then return end
|
||||||
|
if not hintVisible then return end
|
||||||
|
if built then return end
|
||||||
|
if input.KeyCode ~= Enum.KeyCode.E then return end
|
||||||
|
built = true
|
||||||
|
__rbxl_hud_set("g44_slot_${sid}_hint", nil)
|
||||||
|
-- Создаём башню — жёлтый цилиндр над площадкой
|
||||||
|
local tower = Instance.new("Part")
|
||||||
|
tower.Shape = Enum.PartType.Cylinder
|
||||||
|
tower.Size = Vector3.new(1.5, 3, 1.5)
|
||||||
|
tower.Position = Vector3.new(part.Position.X, part.Position.Y + 2.5, part.Position.Z)
|
||||||
|
tower.Color = Color3.fromRGB(255, 204, 51)
|
||||||
|
tower.Anchored = true
|
||||||
|
tower.Parent = workspace
|
||||||
|
local ev = ReplicatedStorage:FindFirstChild("TowerBuilt")
|
||||||
|
if ev then ev:Fire(part.Position.X, part.Position.Z) end
|
||||||
|
end)`;
|
||||||
|
}
|
||||||
|
return overrides;
|
||||||
|
})(),
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
// ИГРЫ 45-50: явных Lua-версий пока нет.
|
||||||
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua
|
// buildGameProject в docsGamesBuilders.js использует generateFallbackLua
|
||||||
// (главный скрипт → показ подсказки + слушает FinishReached →
|
// (главный скрипт → показ подсказки + слушает FinishReached →
|
||||||
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;
|
// победа+конфетти; скрипт на финиш-примитиве → шлёт FinishReached;
|
||||||
|
|||||||
@ -6144,7 +6144,7 @@ game.self.onTouch(() => {
|
|||||||
|
|
||||||
<h3 className="lessonH">Шаг 2. Главный скрипт</h3>
|
<h3 className="lessonH">Шаг 2. Главный скрипт</h3>
|
||||||
<ScriptKind kind="global" />
|
<ScriptKind kind="global" />
|
||||||
<Code>{`// === ИГРА «ГОНКА С ПРЕПЯТСТВИЯМИ» — главный скрипт ===
|
<CodeBoth game="obstacle-race" script="g43_main">{`// === ИГРА «ГОНКА С ПРЕПЯТСТВИЯМИ» — главный скрипт ===
|
||||||
|
|
||||||
let time = 0;
|
let time = 0;
|
||||||
let won = false;
|
let won = false;
|
||||||
@ -6185,7 +6185,7 @@ game.onMessage('finish', () => {
|
|||||||
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>
|
<p>Разберём:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>onTick((dt) ={'>'} ...)</code> — <code>dt</code>
|
<li><code>onTick((dt) ={'>'} ...)</code> — <code>dt</code>
|
||||||
@ -6205,20 +6205,20 @@ game.onMessage('finish', () => {
|
|||||||
|
|
||||||
<h3 className="lessonH">Шаг 3. Скрипты буста, шипа и финиша</h3>
|
<h3 className="lessonH">Шаг 3. Скрипты буста, шипа и финиша</h3>
|
||||||
<ScriptKind kind="object" on="каждый синий буст" />
|
<ScriptKind kind="object" on="каждый синий буст" />
|
||||||
<Code>{`// === Скрипт буста ===
|
<CodeBoth game="obstacle-race" script="g43_boost_1">{`// === Скрипт буста ===
|
||||||
game.self.onTouch(() => {
|
game.self.onTouch(() => {
|
||||||
game.broadcast('boost');
|
game.broadcast('boost');
|
||||||
});`}</Code>
|
});`}</CodeBoth>
|
||||||
<ScriptKind kind="object" on="каждый красный шип" />
|
<ScriptKind kind="object" on="каждый красный шип" />
|
||||||
<Code>{`// === Скрипт шипа-ловушки ===
|
<CodeBoth game="obstacle-race" script="g43_spike_4">{`// === Скрипт шипа-ловушки ===
|
||||||
game.self.onTouch(() => {
|
game.self.onTouch(() => {
|
||||||
game.broadcast('spike');
|
game.broadcast('spike');
|
||||||
});`}</Code>
|
});`}</CodeBoth>
|
||||||
<ScriptKind kind="object" on="зелёный финиш" />
|
<ScriptKind kind="object" on="зелёный финиш" />
|
||||||
<Code>{`// === Скрипт финиша ===
|
<CodeBoth game="obstacle-race" script="g43_finish">{`// === Скрипт финиша ===
|
||||||
game.self.onTouch(() => {
|
game.self.onTouch(() => {
|
||||||
game.broadcast('finish');
|
game.broadcast('finish');
|
||||||
});`}</Code>
|
});`}</CodeBoth>
|
||||||
<Note>
|
<Note>
|
||||||
<code>setSpeed</code> — множитель скорости. 1 — обычная,
|
<code>setSpeed</code> — множитель скорости. 1 — обычная,
|
||||||
1.8 — быстро, 0.5 — медленно. После эффекта всегда
|
1.8 — быстро, 0.5 — медленно. После эффекта всегда
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user