76 Commits

Author SHA1 Message Date
min
e78f585fd4 docs(47) + feat(g48): «Мультиплеер: Салки»
g47 docs: CodeBoth main+btn_1+finish.

g48 паритет (упрощённый — без MP-API):
- __rbxl_show_text 'Опубликуй игру для игры с друзьями'
- __rbxl_hud_set 'info' 50,8 'Игроков в комнате: N' (Players:GetPlayers)
- Players.PlayerAdded → '<имя> присоединился!' + refresh
- Players.PlayerRemoving → refresh
- task.delay 2 показывает правила
- В одиночке роли не назначаются (нет game.room API в shim)
2026-06-09 23:15:11 +03:00
min
57c5779644 docs(46) + feat(g47): «Квест-побег»
g46 docs: CodeBoth 4 скрипта (main+cube+up1+up2).

g47 паритет:
- TOTAL=3, pressed/escaped counters
- BindableEvents ButtonPressed/Escape
- 3 g47_btn_N: Heartbeat distance(3) + '[E] Нажать кнопку'
  E → used=true + Color зелёный + ButtonPressed:Fire
- При pressed>=3: tween двери Position.Y+6 + CanCollide=false + win sound
- g47_finish: Touched → Escape:Fire → 'Победа! Сбежал!' + confetti
2026-06-09 23:12:28 +03:00
min
d758fdfbe6 docs(45) + feat(g46): «Кликер»
g45 docs: CodeBoth g45_main.

g46 паритет (заменил simpleClicker fallback):
- GOAL=200, points/perClick/autoIncome
- Heartbeat: каждую секунду points += autoIncome → checkWin
- BindableEvents CubeClicked/BuyPower/BuyAuto
- g46_cube: ClickDetector → CubeClicked:Fire + sparks
- g46_up1/up2: Heartbeat distance(3) + '[E] Купить ...' + InputBegan E
- buyPower: -20 + perClick+=2
- buyAuto: -40 + autoIncome+=3
2026-06-09 23:07:25 +03:00
min
1c5e5fe5bb 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
2026-06-09 23:04:21 +03:00
min
dc669a51f4 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
2026-06-09 23:00:37 +03:00
min
019068cffa 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'.
2026-06-09 22:55:54 +03:00
min
4085fce0d3 docs(41) + feat(g42): «RPG-деревня»
g41 docs: CodeBoth 4 скрипта (main+coin_10+cp+finish).

g42 паритет:
- 2 NPC (Староста character-a, Кузнец character-b)
- stage 0→1→2→3 цепочка
- BindableEvents ElderTalk/TakeAmulet/SmithTalk
- g42_elder/smith: Heartbeat distance(4) + '[E] Поговорить'
  InputBegan E → Fire
- g42_amulet: Touched → TakeAmulet:Fire + Destroy
- stage 1 elder: 'Найди амулет'
- stage 1 amulet: __rbxl_inventory_define/add 'amulet'
- stage 2 smith: inventory_remove + 'Победа!' + confetti
- Прочие говорят соответствующую реплику
2026-06-09 22:52:22 +03:00
min
e75121cb3d docs(40) + feat(g41): «Платформер-приключение»
g40 docs: CodeBoth g40_main.

g41 паритет:
- Heartbeat py<-3 → LoadCharacter + lose
- BindableEvents CoinCollected/CheckpointReached/TreasureFound
- 5 g41_coin_N (id 10..14): Touched → CoinCollected:Fire + Destroy
- g41_cp: Touched → CheckpointReached:Fire → setSpawn(-0.5,7,28)
- g41_finish: Touched → TreasureFound:Fire → 'Победа! Сокровище и N монет!' + confetti
2026-06-09 22:49:14 +03:00
min
cce9d2e293 docs(39) + feat(g40): «Выживание от волн»
g39 docs: CodeBoth main+spot_1.

g40 паритет:
- WAVES=3, count=wave+2 (3,4,5 врагов)
- Враги по кругу radius=10 (cos/sin), npc.follow('player')
- BindableEvent EnemyClicked(ref)
- __rbxl_npc_on_click(ref, fn) — каждый враг шлёт ref в общий event
- Главный проверяет dist<5, npc.remove + explosion + aliveInWave--
- 0 живых → task.delay 2 startWave (forward-decl)
- 3 волны → 'Победа!' + confetti
2026-06-09 22:46:21 +03:00
min
f7b296f43b fix(g39): 3D-дистанция для distance-check мест
8 мест-призраков стоят один над другим (x=0, z=0, y=1,3,5,...).
По X+Z они в (0,0) → все 8 видят hintVisible=true одновременно
→ нажатие E срабатывало у всех сразу, башня строилась за раз.

Фикс: 3D-дистанция (учитываем dy).
2026-06-09 22:43:40 +03:00
min
b42685521c docs(38) + feat(g39): «Башня — стройка»
g38 docs: CodeBoth main+tile_1.

g39 паритет:
- STEPS=8, placed counter
- BindableEvent BlockPlaced(n)
- 8 g39_spot_N: Heartbeat distance(4) → '[E] Поставить блок'
  E → CanCollide=true, Transparency=0, Color коричневый,
  BlockPlaced:Fire(n)
- Главный: n=placed+1? placed++; иначе 'Сначала поставь ниже!'
- 8 → 'Победа!' + confetti
2026-06-09 22:41:55 +03:00
min
0fcc5b85d0 docs(37) + feat(g38): «Музыкальная игра»
g37 docs: CodeBoth main+spike_1+cp+finish.

g38 паритет:
- SOUNDS [coin,jump,click,hit], SEQ [1,3,2,4,1]
- task.delay 1 + i*0.8 → play sound + 'Нота N из 5'
- После последней task.delay → canPress=true + 'Повтори!'
- BindableEvent NotePressed(n)
- 4 g38_tile_N: Heartbeat distance(3) → '[E] Сыграть ноту'
  E → tileSound + sparks + NotePressed:Fire(n)
- Правильная → playerStep++, при #SEQ → win + confetti
- Ошибка → playerStep=0 + lose + 'Слушай снова'
2026-06-09 22:35:40 +03:00
min
f0025f0dad docs(36) + feat(g37): «Полоса препятствий»
g36 docs: CodeBoth main+box_1.

g37 паритет:
- task.delay 0.2 ДвижПлатформа yoyo loop (x: -0.5 ↔ 3, 2с)
- Heartbeat: py<-3 → LoadCharacter + lose
- BindableEvents CheckpointReached/FinishReached
- 6 g37_spike_N: Touched → damage(25) + hit sound (i-frames 0.5с)
- g37_cp: Touched → CheckpointReached:Fire → setSpawn(-0.5,1,24)
- g37_finish: Touched → FinishReached:Fire → win + confetti

Shim: __rbxl_set_spawn(x,y,z).
2026-06-09 22:32:55 +03:00
min
b0bdfb6e29 docs(35) + feat(g36): «Головоломка с ящиками»
g35 docs: CodeBoth g35_main.

g36 паритет:
- onPlate[3] флаги, при всех true → win
- BindableEvent BoxMoved(i, on)
- g36_box_N: Heartbeat distance-check(3) → '[E] Двинуть ящик',
  E: cell++ (wrap), TweenService Position.Z=ROW[cell], 0.4с,
  Fire(i, newZ == 6)
2026-06-09 22:29:36 +03:00
min
92a9ef220d docs(34) + feat(g35): «Прятки от NPC»
g34 docs: CodeBoth main+plant_1.

g35 паритет:
- SURVIVE=40c, NPC 'Искатель' speed=3 follow('player')
- __rbxl_timer_set каждый кадр
- dist<1.7 → LoadCharacter + 'Найден!' + lose (с throttle 2с)
- time>=40 → npc.stop + 'Победа!' + confetti + win
2026-06-09 22:25:20 +03:00
min
3e572e1136 docs(33) + feat(g34): «Сбор урожая»
g33 docs: CodeBoth g33_main.

g34 паритет:
- BindableEvent Harvested → score++ + coin sound, 6 → win + confetti
- 6 g34_plant_N: TweenService 5с растёт (Size+Position),
  Completed → ripe=true + Color жёлтый
- Heartbeat distance-check (3) → __rbxl_hud_set '[E] Собрать'
- E: не ripe → 'Ещё не выросло!'; ripe → Harvested:Fire + Destroy
2026-06-09 22:22:03 +03:00
min
f3b0cabdbd docs(32) + feat(g33): «Платформер с боссом»
g32 docs: CodeBoth main+cp_1.

g33 паритет:
- Heartbeat: py<-3 → LoadCharacter + lose
- При pz>24 + py>5 (на арене) — spawnNpc 'БОСС' hp=120 speed=2
- task.delay npc.follow('player') + setLabel 'БОСС HP: 120'
- __rbxl_npc_on_click(bossRef, onBossHit):
  dist<5 → bossHp -= 20 + npc.damage + setLabel + sparks + hit
- __rbxl_npc_on_death → clear_label + 'Победа!' + confetti + win
2026-06-09 22:18:18 +03:00
min
4ca3800e49 docs(31) + feat(g32): «Гонка с кругами»
g31 docs: CodeBoth для g31_main.

g32 паритет:
- LAPS=2, CP_COUNT=4, nextCp/lap/time/won
- __rbxl_timer_set — паритет с game.ui.timer=N (формат mm:ss)
- __rbxl_hud_set 'race' — постоянная надпись 'Круг N/2 • чекпоинт M/4'
- Heartbeat: time += dt → timer update
- BindableEvent CheckpointReached(num)
- 4 g32_cp_N: Touched → CheckpointReached:Fire(N)
- При 2 кругах → 'ФИНИШ! Xc' + showText + confetti + win

Shim: __rbxl_timer_set(seconds).
2026-06-09 22:15:16 +03:00
min
901c249c29 docs(29) + feat(g31): «Защита базы»
g30 docs CodeBoth 4 скрипта.

g31:
- killed counter (GOAL=12) + leaked (MAX_LEAK=5)
- Heartbeat spawn враг каждые 2с в (random(-8,8), 1, 38),
  spawn 'character-b' speed 2.5
- task.delay 0.3 npc.moveTo(0, 2) — к базе
- __rbxl_npc_on_click(ref, fn) → шлёт ref в общий BindableEvent
- При клике главный скрипт проверяет dist<5, наносит урон
- Каждые 0.4с проверка прорыва (z<4) → leaked++ + lose sound
- 12 убитых → 'Победа!' + confetti
- 5 прорывов → 'База разрушена!'

Shim: __rbxl_npc_moveto/__rbxl_npc_remove.
2026-06-09 22:11:15 +03:00
min
f69df55e3b docs: CodeBoth урока 29 + feat(g30): полный паритет «Квест с заданиями»
g29 docs: 5 скриптов под CodeBoth (main, coin_1, shop, door, finish).

g30 паритет:
- stage 0→1→2→3→4 (talk/coin/flag/talk)
- setObjective через __rbxl_hud_set 50,8 цвет/размер
- NPC 'Старейшина' spawnNpc + npc_say на этапах
- BindableEvents Talk/CoinDone/FlagDone
- g30_npc: Heartbeat distance-check (4) + '[E] Поговорить' + InputBegan E
- g30_coin: Touched → CoinDone:Fire + Destroy (taken-флаг)
- g30_flag: Touched → FlagDone:Fire (fired-флаг)
- На stage 3: showText + confetti + win sound
2026-06-09 22:06:27 +03:00
min
5186ee3b70 feat(g29): добавлен NPC-продавец за прилавком с меткой 'Продавец' 2026-06-09 22:03:06 +03:00
min
facd6aa837 feat(g29): полный паритет «Магазин»
JS:
- 7 монеток onTouch → broadcast coin → score++, coin sound
- прилавок onInteract E (4) → broadcast buy → coins>=5? покупка ключа
  + inventory.add('Ключ') + 'Куплен!' + win sound; иначе lose+'Мало!'
- дверь onInteract E (4) → broadcast open-door → hasKey? tween y+6;
  иначе 'Дверь заперта'
- финиш → broadcast win → 'Победа!' + confetti

Lua (паритет):
- BindableEvents CoinCollected/BuyKey/OpenDoor/WinReached
- __rbxl_score_set, __rbxl_show_text, __rbxl_inventory_define/add
- 7 g29_coin_N: Touched → CoinCollected:Fire + Destroy
- g29_shop, g29_door: Heartbeat distance-check (4) + __rbxl_hud_set
  '[E] Купить ключ (5 монет)' / '[E] Открыть дверь' + InputBegan E
- g29_main обрабатывает все события, при покупке: TweenService двери
  Position.Y+6 + CanCollide=false
- g29_finish: Touched → WinReached:Fire (fired-флаг)
2026-06-09 22:00:35 +03:00
min
a72101a29a fix(g28): ClickDetector вместо part.Clicked (как в Тире)
part.Clicked не существует — был fallback try/catch с молчаливым
падением. ClickDetector + MouseClick — рабочий путь (используется
в игре 15 «Тир»).
2026-06-09 21:57:38 +03:00
min
095a79cab4 feat(g28): полный паритет «Призрачные стены»
JS:
- showText 'Кликай по фиолетовым стенам — пройди сквозь!'
- onMessage 'win' → 'Победа!' + win + confetti
- 4 стены: onClick → passThrough + opacity=0.25 + click sound + showText
- финиш: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text + Sounds
- BindableEvent WinReached
- 4 g28_wall_N: part.Clicked → CanCollide=false + Transparency=0.75 +
  click Sound + showText 'Стена стала призрачной!'
- g28_finish: Touched → WinReached:Fire (fired-флаг)
2026-06-09 21:54:09 +03:00
min
17417b1b33 feat(g27): полный паритет «Двойной прыжок»
JS:
- player.setDoubleJump(true)
- showText 'Жми Space ДВАЖДЫ — двойной прыжок!'
- onTick: y<-3 → respawn + lose
- onMessage 'win' → 'Победа!' + win + confetti
- финиш: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_set_double_jump(true) — паритет с player.setDoubleJump
- __rbxl_show_text + Sounds
- Heartbeat: player_y < -3 → LoadCharacter + lose
- BindableEvent WinReached
- g27_finish: Touched → WinReached:Fire (fired-флаг)

Shim добавил __rbxl_set_double_jump(bool) → cmd 'player.setDoubleJump'.
2026-06-09 21:50:08 +03:00
min
2c324fa576 feat(g26): полный паритет «Магнит монет»
JS:
- showText 'Подходи к монеткам — они притянутся!'
- ui.score=0 → каждая собранная монета +1
- onMessage 'coin' → score++ + 'coin' sound, при score>=TOTAL победа + confetti
- 8 монет: onTick → dist<6 → tween к игроку (0.5с), dist<1.2 → delete + broadcast

Lua (паритет):
- __rbxl_show_text + Sounds
- __rbxl_score_set(N) — паритет с game.ui.score=N (ui.set id=__score)
- BindableEvent CoinCollected
- 8 g26_coin_N: Heartbeat → dist<6 TweenService к Vector3(px,py+1,pz),
  dist<1.2 → ev:Fire + Destroy

Shim добавил __rbxl_score_set(value).
2026-06-09 21:47:20 +03:00
min
189a23ff7c fix(g25): передаём lookAt-точки чтобы камера вращалась к финишу
cameraCutscene без lookAt держит угол постоянным (setTarget не зовётся).
Добавил 3-й arg в __rbxl_camera_cutscene и 4 lookAt-точки указывающие
вдоль пути — камера плавно поворачивается к финишу.
2026-06-09 21:44:14 +03:00
min
d4b84cf73d feat(g25): полный паритет «Камера-облёт»
JS:
- camera.cutscene 4 точки + segDuration=1.8
- onCutsceneDone → showText 'Вперёд, к зелёному финишу!'
- onMessage 'win' → 'Победа!' + win + confetti
- finish: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_camera_cutscene('x1,y1,z1, x2,y2,z2, ...', segDuration)
  Парсит CSV → отдаёт в GameRuntime cmd 'camera.cutscene'.
- __rbxl_on_cutscene_done(fn) — регистрация cb.
  В fireGlobalEvent при p.type='cutsceneDone' фейерим все cb.
- BindableEvent WinReached
- g25_finish: Touched → WinReached:Fire (fired-флаг)

CSV вместо массива объектов — wasmoon через C-boundary
плохо отдаёт массивы таблиц.
2026-06-09 21:41:02 +03:00
min
5aec627b17 feat(g24): полный паритет «Падающий мост»
JS:
- showText 'Беги по мосту — доски рушатся!'
- onTick: p.y < -3 → respawn + 'Упал в пропасть! Снова.' + lose
- onMessage 'win' → 'Победа!' + win + confetti
- 18 досок: onTouch → click sound + delete через 1с
- финиш: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text + Sounds
- Heartbeat: player_y < -3 → LoadCharacter + lose
- BindableEvent WinReached
- g24_plank_1..18: единый скрипт (IIFE генерит 18 ключей):
  Touched → click Sound + Debris:AddItem(part, 1)
- g24_finish: Touched → WinReached:Fire (fired-флаг)
2026-06-09 21:37:05 +03:00
min
eb04da6348 feat(g23): полный паритет «Переключатели»
JS:
- showText 'Дёрни рычаги в нужном порядке (E)'
- onMessage 'lever' с {num} → click sound + pressed.push + проверка
  совпадения с ORDER=[2,3,1]. Ошибка → reset + 'Неверно!' + lose
  Полная последовательность → 'Верно! Дверь открыта' + win + tween двери
- onMessage 'win' → 'Победа!' + win + confetti
- лычаги: onInteract E (distance=3) → broadcast 'lever' {num}
- финиш: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text + Sounds
- BindableEvents LeverPulled (с num аргументом) + WinReached
- g23_main: проверка порядка + tween двери (Position.Y+6) + CanCollide=false
- 3 g23_lever_N: Heartbeat distance-check (3), __rbxl_hud_set
  '[E] Дёрнуть рычаг N' в нижней части экрана.
  UserInputService.InputBegan E → LeverPulled:Fire(n)
- g23_finish: Touched → WinReached:Fire (fired-флаг)
2026-06-09 21:33:12 +03:00
min
59769932e5 feat(g22): полный паритет «Зона опасности»
JS:
- showText 'Пробеги через красную зону к финишу!'
- every(0.6): если inZone — damage(12) + hit
- onMessage 'zone-enter' → inZone=true + 'Опасно! Беги быстрее!'
- onMessage 'zone-leave' → inZone=false
- onMessage 'win' → 'Победа!' + win + confetti
- g22_zone: onTouch → 'zone-enter', onUntouch → 'zone-leave'
- g22_heal: onTouch → heal(60) + '+60 HP' + pickup + delete
- g22_finish: onTouch → 'win'

Lua (паритет):
- __rbxl_show_text + Sounds
- BindableEvents ZoneEnter/ZoneLeave/WinReached
- Heartbeat-таймер 0.6с для урона пока inZone
- g22_zone: Touched/TouchEnded → ev:Fire
- g22_heal: Touched → __rbxl_heal_player(60) + pickup Sound + Destroy
- g22_finish: Touched → WinReached:Fire

Shim добавил __rbxl_heal_player(amount) → cmd 'player.heal'.
2026-06-09 21:29:32 +03:00
min
ea1308d539 feat(g21): полный паритет «Преследователь» + npc.follow/stop/pos в shim
JS:
- spawnNpc 'Охотник' speed=4 follow('player')
- onTick: dist(player,enemy) < 1.6 → respawn + 'Пойман!' + lose
- onMessage 'win' → enemy.stop() + 'Победа!' + win + confetti
- g21_finish: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text + Sounds
- __rbxl_spawn_npc('character-b', ..., 'Охотник', 100, 4)
- __rbxl_npc_follow(ref, 'player') — велим NPC следовать за игроком
- Heartbeat: __rbxl_npc_x/z для расстояния, при <1.6 → LoadCharacter
  + 'Пойман!' + lose Sound (с throttle 2с)
- BindableEvent WinReached + g21_finish.Touched → ev:Fire
- При победе: npc_stop + showText + win + confetti

Shim хелперы:
- __rbxl_npc_follow(ref, target='player')
- __rbxl_npc_stop(ref)
- __rbxl_npc_x/y/z(ref) — позиция NPC
- api.updateNpcPos(localRef, x, y, z) — GameRuntime синкает каждый кадр

GameRuntime.tick собирает позиции всех NPC из npcManager.npcs через
_localToReal и шлёт sb.api.updateNpcPos.
2026-06-09 21:23:53 +03:00
min
58fbc9d6e6 fix(g20): метка height 2.5 — над health-bar 2026-06-09 21:18:50 +03:00
min
4320c6adeb fix(g20): уменьшил зазоры метки и health-bar
LabelManager использует opts.height как ГАП от верха AABB до плашки,
а не абсолютную высоту. Передавал 3 — метка летела далеко вверх.
Стало 0.3 — небольшой зазор над головой.

Health-bar опустил с y+2.4 до y+1.9 — ближе к голове.
2026-06-09 21:17:16 +03:00
min
7595668b03 fix(g20): метку HP подняли с 3 до 4 — не перекрывается health-bar
Health-bar NPC рендерится на y+2.4 при уроне. Метка height=3 была
слишком близко — health-bar заходил поверх текста. Высота 4 даёт
зазор в ~1.6 над health-bar.
2026-06-09 21:13:38 +03:00
min
9eebbd302e feat(g20): клик по NPC через pick (как Тир ClickDetector)
Раньше: проверка дистанции до врага при ЛКМ через InputBegan
+ MouseButton1. Никак не работало из-за десятка причин.

Теперь как в Тире:
- BabylonScene._meshToTarget теперь возвращает {kind:'npc', id:N}
  для меша с metadata.npcId.
- routeGlobalEvent('click', {target}) — этим уже шлёт в Lua-shim
  с target.
- Shim добавлен __rbxl_npc_on_click(ref, fn) — регистрация callback'а.
  В fireGlobalEvent при type='click'+target.kind='npc' резолвим
  локальный ref и фейерим cb.
- В скрипте игры 20 регистрируем callback на каждого врага.
  Клик ЛКМ по NPC (raycast попадает в мешa NPC) → callback → урон.
2026-06-09 21:10:22 +03:00
min
8a3405e34a feat(g20): полный паритет «Имена над врагами» + shim NPC API
JS:
- 3 NPC (Гоблин, Скелет, Орк) через spawnNpc('character-b')
- setLabel над каждым с HP, обновляется при уроне
- ЛКМ → бьём ближайшего врага в радиусе 4 (damage 30)
- onDeath → clearLabel + hit sound + при 0 врагов — победа+confetti

Расширения shim/runtime:
- __rbxl_spawn_npc уже было, добавил api._localToRealNpc Map
- __rbxl_npc_damage(ref, amount) → cmd 'npc.damage'
- __rbxl_set_label(ref, text, color, height) → cmd 'scene.setLabel'
- __rbxl_clear_label(ref) → cmd 'scene.clearLabel'
- __rbxl_npc_on_death(ref, fn) — регистрирует cb. shim слушает global
  event 'npcDeath' (resolveTweenTarget теперь поддерживает kind='npc')
  и зовёт зарегистрированные cb с подходящим ref (local или real).
- GameRuntime.npc.spawn.then синхронизирует _localToRealNpc в Lua-sb.

Lua-скрипт игры 20 (паритет):
- showText 'Победи всех врагов! Кликай по ним'
- 3 спавна с метками HP над головой
- UserInputService.InputBegan MouseButton1 → ближайший враг в r=4 → -30hp
- На смерть: clearLabel + при 0 — Победа + win Sound + confetti
2026-06-09 20:34:20 +03:00
min
c489a31854 feat(g19): полный паритет «Лифт» — Heartbeat yo-yo
Игра 19 падала на WaitForChild + Vector3 + (оператор не поддержан).

Lua паритет с JS:
- showText 'Встань на синий лифт — он повезёт наверх'
- task.delay 0.2c для FindFirstChild('Лифт') (WaitForChild может зависать)
- Heartbeat yo-yo по Y: startY (1) ↔ TOP_Y (12.3) с периодом 7с
  (3.5с вверх + 3.5с вниз через треугольную функцию)
- Vector3.new напрямую (без +)
- Респаун при y<-3
- BindableEvent WinReached + g19_finish.Touched → ev:Fire
- При победе: showText 'Победа!' + win Sound + confetti
2026-06-09 20:26:19 +03:00
min
430b9eddcd feat: игра 18 «Качели» Lua + кастомная модалка выхода
Игра 18:
- Падало на WaitForChild + task.spawn + Completed:Wait (yield-across-C).
- Полный переписан: Heartbeat-таймер раскачивает swing по синусу
  с амплитудой 4 и периодом 2.8с (вместо TweenService).
- task.delay 0.2с чтобы дождаться появления свинга в scene.
- Vector3.new напрямую (без оператора + который не работает).
- BindableEvent WinReached + g18_finish.Touched → ev:Fire.
- Полный паритет: showText, win Sound, confetti, respawn при y<-3.

Модалка выхода:
- Заменил window.confirm в KubikonEditor.handleBack на ConfirmModal.
- 3 варианта: Сохранить и выйти / Выйти без сохранения / Отмена.
- ConfirmModal расширен onCancel prop (отделяет 'cancel-кнопка'
  от 'клик мимо/Escape').
2026-06-09 20:22:16 +03:00
min
1345f51436 feat(g17): полный паритет с JS «Ключ и сундук»
JS:
- showText 'Найди ключ и открой сундук!'
- onMessage 'takeKey' → inventory.add('Ключ') + 'Ты нашёл Ключ!' + pickup
- onMessage 'openChest' → если has('Ключ'): 'Победа!' + win + confetti
                         иначе: 'Сундук заперт. Сначала найди ключ.'
- key: onTouch → broadcast 'takeKey' + delete
- chest: onInteract E (distance=4) → broadcast 'openChest'

Lua (паритет, юзер указал на отсутствие надписей/инвентаря/звуков):
- __rbxl_show_text для всех подсказок (вместо print)
- __rbxl_inventory_define('key','Ключ') → итем в hotbar инвентаря
- __rbxl_inventory_add('key',1) при подборе (вместо leaderstats BoolValue)
- __rbxl_inventory_has('key') проверка ключа в сундуке
- Sounds 'pickup'/'win'/'lose' (раньше только print)
- BindableEvents TakeKey + OpenChest
- g17_key: Touched → ev:Fire + Destroy (с taken-флагом)
- g17_chest: убран ошибочный Touched-handler.
  Heartbeat distance-check как в Торговце:
  при near=true → __rbxl_hud_set '[E] Открыть сундук'
  UserInputService E → OpenChest:Fire
- При победе: confetti над игроком + win Sound
2026-06-09 20:13:52 +03:00
min
2979104931 fix(g16): урон только когда игрок РЕАЛЬНО в лаве (не над островком)
Раньше: фильтр по Y-капсулы не работал — игрок стоит на островке на
realPos.y=1.54 > порога 1.35, фильтр пропускал.

Стало: проверяем где игрок по X/Z. Если над островком (любой из 6) или
над финишной площадкой — пропускаем урон. Иначе — урон каждый кадр пока
Touched активно (RunService.Heartbeat). У damage есть i-frames ~0.5с
так что урон ~2/сек.

Также: переход с Touched-разово на Heartbeat-постоянно. Раньше Touched
срабатывал только при ВХОДЕ в лаву — если игрок стоит в лаве долго,
TouchEnded не вызывался, и урон шёл только раз. Теперь Touched/TouchEnded
выставляют inLava-флаг, Heartbeat считает урон каждый кадр.
2026-06-09 20:09:07 +03:00
min
f0f739071a feat(g16): полный паритет «Лава-пол» + защита от ложного хитбокса
JS:
- showText 'Прыгай по островкам! Лава жжёт!'
- onTick: y<-2 → respawn
- onMessage 'win' → showText + win + confetti
- g16_lava: onTouch → damage(20) + hit sound
- g16_finish: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text + win Sound + confetti
- Heartbeat: __rbxl_player_y() < -2 → LoadCharacter
- BindableEvent WinReached + g16_finish.Touched → ev:Fire
- g16_lava: __rbxl_damage_player(20) + hit Sound
  ВАЖНО: лава-зона на y=1±0.3 (верх 1.3), островки на y=1.2±0.3 (верх 1.5).
  AABB-зоны пересекаются → BabylonScene touch-detector с EPS=0.25
  активировал лаву даже когда игрок стоит на островке.
  Защита: если игрок выше Y=1.35 (стоит на островке/финише) — пропускаем
  урон. Урон срабатывает только когда игрок реально в лаве.

Shim добавил:
- __rbxl_damage_player(amount) → cmd player.damage (с i-frames)
2026-06-09 20:03:50 +03:00
min
8abbde9d67 feat(g15): полный паритет «Тир» + ClickDetector в shim
JS:
- 8 мишеней-сфер на постаментах
- ui.score, showText 'Кликай по красным мишеням!'
- target: onClick → explosion particles + delete + broadcast 'hit'
- main: onMessage 'hit' → score++ + hit sound + при 8 победа+confetti

Lua-shim расширение:
- Instance.new('ClickDetector') создаёт инстанс с MouseClick signal
- Когда clickDet.Parent = part → Proxy.set регистрирует
  part._clickDetector = clickDet
- fireTargetEvent при kind='click' фейерит part._clickDetector.MouseClick.Fire

Lua-скрипт игры 15:
- ScreenGui 'Мишени: N / 8'
- BindableEvent TargetHit
- 8 g15_target_N (ids 2,4,6,8,10,12,14,16): ClickDetector с
  MaxActivationDistance=50 → MouseClick → spawn explosion + Destroy + Fire
- main: hit Sound, при 8 — win Sound + showText + confetti
2026-06-09 19:52:16 +03:00
min
7f3b81a531 feat: умный авто-fallback Lua для всех игр без явной реализации
Раньше для игр 15-50 при открытии 'Открыть мою копию на Lua' юзер
получал TODO-заглушки которые ничего не делали (или simpleMain
который только print). Каждая новая игра без явного override
была полностью неиграбельной.

Новый generateFallbackLua(s, gameTitle) в buildGameProject:

Главный скрипт (target=null):
  - __rbxl_show_text(gameTitle) подсказка
  - Слушает BindableEvent FinishReached → win Sound + Победа! + confetti

Скрипт-финиш (target=primitive с именем 'Финиш'/'ФинишЗона'/'Final'):
  - Touched → создаёт/находит BindableEvent FinishReached → Fire
  - fired-флаг чтобы 1 раз

Прочие target-скрипты:
  - Touched → красит примитив зелёным (визуальный feedback)
  - touched-флаг

Удалил статичные simpleMain stub-ы для игр 31-50 — теперь они
используют умный fallback. Когда дописываем полную Lua-версию
игры — добавляем явный override в LUA_OVERRIDES, fallback
автоматически перестаёт использоваться.

Это даёт минимум: победа на финише + цвет на касании во всех
35 не-готовых играх (15-50).
2026-06-09 19:43:20 +03:00
min
cc5717f5a3 feat(lua-games): полный паритет для игры 14 «Собери по тегам»
JS:
- 7 жёлтых звёзд-конусов + 5 синих кубов-обманок
- showText 'Собери все ЖЁЛТЫЕ звёзды!'
- main помечает звёзды тегом 'звезда' с задержкой 0.2с
- onMessage 'collected' → score++ + при 0 left — победа+confetti
- star: onTouch → untag + delete + broadcast 'collected'

Lua (паритет):
- 7 g14_star_N через генератор (раньше был один g14_star)
- ScreenGui 'Звёзды: N / 7' счётчик
- BindableEvent StarCollected
- main: task.delay(0.2) → AddTag всем 7 звёздам через workspace:FindFirstChild
- При collect → coin Sound + GetTagged-проверка left==0 → win+confetti
- g14_star_N: RemoveTag + Destroy + ev:Fire (с picked-флагом)
2026-06-09 19:38:55 +03:00
min
b7b3c1eb81 fix(g13): подсказки [E] через HUD ui.set — паритет с JS
Юзер: подсказка в Lua была слева от центра, белая. В JS — точно по
центру внизу, жёлтая.

Корень: я использовал BillboardGui+TextLabel с явной Position, но
GUI-shim позиционирует label некорректно (UDim2 offset плохо
интерпретируется → плашка плыла).

Фикс: используем тот же механизм что JS — game.ui.set (HUD через
React). Добавил хелпер __rbxl_hud_set(id, text, x, y, color, size)
шлющий 'ui.set' cmd, GameRuntime пробрасывает в _onHud → GameHud.jsx
рендерит точно как для JS-скриптов.

В g13_counter/g13_door: при near=true → hud_set с (50, 75, #ffe44a, 20)
(центр по X, ниже центра по Y, жёлтый — точно как JS interact hint).
При выходе из зоны → hud_set(id, nil) убирает.
2026-06-09 19:34:32 +03:00
min
e8bfdda380 fix(g13): подсказки выше хотбара (0.6 центра вместо низа)
Прошлая позиция (1, -120) перекрывала хотбар инвентаря.
Новая (0.5, -160; 0.6, 0) — выше середины экрана, не мешает.
2026-06-09 19:09:34 +03:00
min
47ad608182 feat(g13): NPC-торговец + инвентарь как в JS
Юзер указал что в Lua-версии было отступление от JS:
- Торговец нарисован примитивами вместо character-a скина
- Ключ показан надписью в HUD вместо инвентаря-hotbar

Добавил в shim хелперы паритета:
- __rbxl_spawn_npc(modelType, x,y,z, name?, hp?, speed?) → cmd npc.spawn
  Возвращает локальный ref для дальнейших команд.
- __rbxl_npc_say(ref, text, duration) → cmd npc.say
- __rbxl_inventory_define(itemId, name, color) → cmd items.define
- __rbxl_inventory_add(itemId, count) → cmd inv2.add (показывает в hotbar)
- __rbxl_inventory_has(itemId) → проверка локального кеша
- __rbxl_inventory_remove(itemId, count) → cmd inv2.remove

Lua-скрипт игры 13:
- spawnNpc 'character-a' за прилавком как в JS
- inventory_define('key', 'Ключ') → hotbar
- При разговоре: npc_say + inventory_add('key', 1)
- При двери: проверяем inventory_has('key')
2026-06-09 19:05:55 +03:00
min
fb390f402c fix(g13): торговец-фигурка + подсказки внизу экрана
1) Торговец-NPC отсутствовал. Спавним фигурку из 3 частей:
   - Тело (синий куб) на (0, 2.0, 5) — за прилавком
   - Голова (бежевая сфера) на (0, 3.2, 5)
   - Шляпа (коричневый цилиндр) на (0, 3.8, 5)

2) Подсказки [E] плыли в центр экрана. Задал явную позицию:
   нижняя часть, по центру, с тёмной плашкой и тенью.
   Применил к g13_counter и g13_door.
2026-06-09 18:59:54 +03:00
min
e163fe9770 feat(lua-games): полный паритет для игры 13 «Торговец»
JS:
- spawnNpc торговец, prompts E:
  Прилавок onInteract → broadcast 'talk' → выдать ключ через inventory
  Дверь onInteract → broadcast 'openDoor' → проверка inventory.has
  → tween двери (y:9)
- Финиш onTouch → broadcast 'win' → confetti

Lua (паритет, NPC через статичный прилавок):
- ScreenGui 'Ключа нет' / 'У тебя есть Ключ' (вместо inventory)
- BindableEvents TalkTrader / OpenDoor / WinReached
- g13_counter: BillboardGui '[E] Поговорить с торговцем' в радиусе 4
  + UserInputService E → TalkTrader:Fire
- g13_door: BillboardGui '[E] Открыть дверь' в радиусе 4
  + E → OpenDoor:Fire (или 'Дверь заперта' если ключа нет)
- g13_main:
  TalkTrader → если ключа нет: hasKey=true + 'Привет!' + pickup Sound
  OpenDoor → если hasKey: tween двери +6 по Y + win Sound
  WinReached → 'Победа!' + confetti
- g13_finish: Touched → WinReached:Fire (fired-флаг)
2026-06-09 18:56:28 +03:00