264 Commits

Author SHA1 Message Date
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
6aaab1a3f1 fix(g20): обрабатываем 'click' (от BabylonScene) как mouseButton1Down
BabylonScene routeGlobalEvent('click') при ЛКМ — это и есть событие
мыши, но shim ждал только 'mouseButton1Down' (от плеера).

Фикс: мапим p.type === 'click' тоже на mouseButton1Down branch.
Внутри уже фейерим Mouse.Button1Down + UserInputService.InputBegan
с UserInputType.MouseButton1.
2026-06-09 21:07:47 +03:00
min
8c32e80f9f fix(g20): UserInputService.InputBegan фейерится на mouseButton1
Раньше при ЛКМ фейерился только playerMouse.Button1Down.Fire(),
а UserInputService.InputBegan/InputEnded — нет. Lua-скрипт игры 20
ловил клик через InputBegan + UserInputType.MouseButton1 → нет
события → урон не наносился.

Фикс: при mouseButton1Down/Up фейерим InputBegan/InputEnded с
InputObject {UserInputType=Enum.UserInputType.MouseButton1} —
ссылка на тот же объект, что Lua использует в сравнении.
2026-06-09 21:05:16 +03:00
min
4644a332e4 fix(g20): метки привязываются к npc.data.rootMesh (модель)
Метки рисовались в куче в (0,0,0) — anchorMesh был сам npc (объект,
не mesh). LabelManager делал plane.parent = anchorMesh, но позиция
не наследовалась.

Фикс: _resolveTweenTarget для 'npc' возвращает data.mesh =
npc.data.rootMesh (реальная модель NPC).
2026-06-09 21:01:58 +03:00
min
56c35273ef fix(g20): _npcCmd ждёт npc_lua_ префикс + sb.api.setNpcLocalRef
_npcCmd проверял только индекс 'npc:_local_' (для JS-воркера),
ref от Lua-shim 'npc_lua_0' пропускался — отложенные setLabel/damage
терялись.

Также: проверка sb.api?._localToRealNpc была false после рефакторинга
(перенёс в local closure). Замена на sb.api?.setNpcLocalRef — публичный
метод.

Убрал debug-логи.
2026-06-09 20:59:30 +03:00
min
65de311d59 debug(g20): лог npc.spawn/damage/setLabel в LuaSharedSandbox onCommand 2026-06-09 20:53:01 +03:00
min
1be06343ec fix(g20): отложенный setLabel для NPC до npcSpawned-резолва
setLabel сразу после spawnNpc возвращал null от _resolveTweenTarget
(маппинг localRef→realId ещё не записан). Метки молча терялись.

Фикс: если ref начинается с npc_lua_ — откладываем через _npcCmd
(re-вызов после npcSpawned). Иначе retry через 0.3с.
2026-06-09 20:41:09 +03:00
min
b83c9bb75c fix(g20): использовать api.setNpcLocalRef вместо прямого access
Lua-runtime FATAL: 'Cannot access api before initialization' — я
обращался к api._localToRealNpc на 1852 до const api = на 2027.

Фикс:
- _localToRealNpc объявил как const до api (доступен в closures)
- api.setNpcLocalRef(localRef, realRef) — публичный метод
- GameRuntime: sb.api.setNpcLocalRef?.(ref, 'npc:'+npcId)
- В fireGlobalEvent npcDeath используем _localToRealNpc напрямую
2026-06-09 20:38:49 +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
3cdbbc5049 docs: CodeBoth для урока 19 «Лифт» (main+finish) 2026-06-09 20:28:37 +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
98e92b23a7 docs: CodeBoth для урока 18 «Качели» (main+finish) 2026-06-09 20:24:48 +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
b5ba62cca8 docs: CodeBoth для урока 17 «Ключ и сундук» (main+key+chest) 2026-06-09 20:15:54 +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
1a6a92b431 docs: CodeBoth для урока 16 «Лава-пол» (main+lava+finish) 2026-06-09 20:11:16 +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
662d0d06e4 docs: CodeBoth для урока 15 «Тир» (main+target) 2026-06-09 20:01:00 +03:00
min
d6a874c8a0 fix(g15): клик в 3-м лице по координатам курсора, не центру
В 3-м лице курсор свободный. _handlePlayClick ВСЕГДА пикал центром
экрана через _pickFromCenter — независимо от того где находится курсор.
Поэтому клик мышкой по мишени не срабатывал — пик шёл из центра.

Фикс: проверяем document.pointerLockElement:
- locked (1-е лицо) → _pickFromCenter (прицел всегда в центре)
- !locked (3-е лицо) → scene.pick(clickX, clickY) по реальной позиции
  курсора при клике.

Убрал debug-логи.
2026-06-09 19:59:38 +03:00
min
12efef7ff5 debug(g15): логи в shim fireTargetEvent + click — найти где обрывается 2026-06-09 19:56:06 +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
2e1ee87ed6 docs: CodeBoth для урока 14 «Собери по тегам» (main+star) 2026-06-09 19:47:55 +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
1a3c8e66e6 docs: CodeBoth для урока 13 «Торговец» (main+counter+door+finish) 2026-06-09 19:37:05 +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
min
e0a457bd7a docs: CodeBoth для урока 12 «Дверь по коду» (main+btn+finish) 2026-06-09 18:55:09 +03:00
min
89baf23877 feat(lua-games): полный паритет для игры 12 «Дверь по коду»
JS:
- CODE=[3,1,4,2], showText 'Нажми кнопки в правильном порядке (E)'
- onMessage 'press' с {num} → click sound, push в entered, проверка
  совпадения с CODE, ошибка → сброс + 'Неверно!' + lose sound
  весь код → 'Код верный! Дверь открывается' + win sound + tween двери
- onMessage 'win' → 'Победа!' + win + confetti
- кнопка: onInteract (E, distance=3) → broadcast 'press' {num}
- финиш: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text всех подсказок + Sound 'click'/'lose'/'win'
- BindableEvent ButtonPress (с num аргументом) + WinReached
- g12_main: tween двери (Position.Y+=6) + CanCollide=false
- 4 g12_btn_N: BillboardGui '[E] Нажать N' видим в радиусе 3
  UserInputService.InputBegan E → ev:Fire(num)
  Hint видимость через Heartbeat + __rbxl_player_x/z
- g12_finish: Touched → ev:Fire WinReached
- При win — confetti
2026-06-09 18:53:22 +03:00
min
b2f6b084df docs: CodeBoth для урока 11 «Эхо-комната» (main+tile+finish) 2026-06-09 18:51:40 +03:00
min
eacc3f990b feat(lua-games): полный паритет для игры 11 «Эхо-комната»
JS:
- 6 цветных плиток-цилиндров со своими звуками
- ui.score, ui.showText 'Наступи на все цветные плитки!'
- onMessage 'step' → score++ + при 6 'Иди на финиш'
- onMessage 'finish' → если < TOTAL то 'Сначала пройди все плитки'
  иначе showText + win + confetti
- g11_tile_N: onTouch → sound + sparks particles, при первом — broadcast
- g11_finish: onTouch → broadcast 'finish'

Lua (паритет):
- ScreenGui 'Плитки: N / 6'
- BindableEvent EchoStep (плитка) + EchoFinish (зона)
- 6 g11_tile_N: каждая со своим Sound (coin/jump/pickup/click/hit/coin)
  + __rbxl_spawn_particles('sparks', x, y+1, z) при касании
  + Throttle 0.4с между звуками + used-флаг
- g11_main: 'Все плитки звучали! Иди на финиш' при 6
  'Сначала пройди все 6 плиток!' если рано пришёл на финиш
  При win — Sound 'win' + confetti
2026-06-09 18:47:57 +03:00
min
3be10c3cf7 docs: CodeBoth для урока 10 «Прыжок-пружина» (main+tramp+finish) 2026-06-09 18:46:08 +03:00
min
50b08b81bc feat(lua-games): полный паритет для игры 10 «Прыжок-пружина»
JS:
- showText 'Прыгай по батутам всё выше!'
- onTick: y<-3 → respawn + lose sound
- onMessage 'win' → showText + win + confetti
- g10_tramp_N: onTouch → player.boostJump(3.2) + jump sound
- g10_finish: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text подсказка + 'Победа!'
- Heartbeat: __rbxl_player_y() < -3 → LoadCharacter + lose Sound
- BindableEvent WinReached + g10_finish.Touched → ev:Fire
- При win — confetti

Добавил хелпер в shim:
- __rbxl_boost_jump(strength) → send 'player.boostJump'
  3.2 = втрое выше обычного прыжка

g10_tramp_4/5/6: Touched → __rbxl_boost_jump(3.2) + jump Sound
с защитой от зацикливания (минимум 0.5с между активациями).
2026-06-09 18:44:34 +03:00
min
4186b49be4 docs: CodeBoth для урока 9 «Светофор» (g9_main + g9_finish) 2026-06-09 18:42:14 +03:00
min
f18835d5e9 fix(lua): player:LoadCharacter() / hrp.Position телепортируют через player._pos
GameRuntime обрабатывал prop:'position' и prop:'respawn' через
player.body.position.set() — но PlayerController хранит позицию в
player._pos (не body). Поэтому LoadCharacter молча ничего не делал.

Фикс: ставим player._pos.set(x, y+halfH, z) + сброс _vy.
Fallback на body если _pos нет.
2026-06-09 18:40:20 +03:00
min
bec3c478e7 feat(lua-games): полный паритет для игры 9 «Светофор»
JS:
- showText 'Зелёный — беги! Красный — замри!'
- light=findOne('Светофор'), green/red/green циклически (3с/2.5с)
- setColor light на красный/зелёный + showText
- onTick: если красный и moved/dt>0.8 — respawn + lose sound
- onMessage 'win' → showText + win + confetti
- g9_finish: onTouch → broadcast 'win'

Lua (паритет):
- __rbxl_show_text для всех подсказок
- Heartbeat-таймер фазы (task.spawn не умеет yield) — переключает
  phase 'green'/'red' каждые GREEN_TIME/RED_TIME
- light=workspace:FindFirstChild('Светофор'), .Color = Color3.fromRGB
- Heartbeat: prevX/prevZ, при phase=red и moved/dt>0.8 → LoadCharacter
  + lose Sound + 'Двинулся на красный!'
- BindableEvent WinReached
- g9_finish: Touched → ev:Fire с fired-флагом
- При победе: win Sound + confetti
2026-06-09 18:35:47 +03:00
min
41e0f7b6a4 feat(wiki): добавил CodeBoth с Lua-параллелью к скриптам игр 1-8
Создан хелпер CodeBoth в docsLessons.jsx: оборачивает <Code> в
<LangTabs js={JS-код} lua={Lua-код из LUA_OVERRIDES}>. Юзер
переключает JS↔Lua вверху урока — код в статье меняется тоже.

Заменены 17 блоков <Code> в уроках игр 1-8 на <CodeBoth>:
- collect-coins (g1_main, g1_coin_1)
- platform-jump (g2_main, g2_finish)
- dont-fall (g3_main, g3_tile_1)
- button-door (g4_main, g4_button, g4_finish)
- maze (g5_main, g5_finish)
- color-tiles (g6_main, g6_tile_1)
- catch-falling (g7_main)
- run-to-finish (g8_main, g8_finish)

Для остальных игр (9-50) остался JS-only Code — заменим
по мере прохождения.
2026-06-09 18:24:16 +03:00
min
73bf9f5c34 feat(lua-games): полный паритет для игры 8 «Беги к финишу»
JS:
- ui.timer + showText 'Беги к зелёному финишу — на время!'
- onTick: time += dt
- onMessage 'finish' → 'Финиш! Время: N сек' + win + confetti
- g8_finish: onTouch → broadcast 'finish'

Lua (паритет):
- ScreenGui+TextLabel секундомер вверху по центру '0.0 сек'
- __rbxl_show_text подсказка
- RunService.Heartbeat: time += dt → label.Text каждый кадр
- BindableEvent FinishReached
- g8_finish: Touched → ev:Fire с fired-флагом
- При финише: 'Финиш! Твоё время: X.X сек' + win Sound + confetti
2026-06-09 18:16:43 +03:00
min
1a174f2854 fix(g7): расширил радиус Touched на 1.2 — куб ловится при сближении
Куб с физикой отталкивается от игрока (DynamicsManager push) и успевает
отскочить до следующего кадра. Строгий AABB ловил только при защемлении
в углу. Расширение SLACK=1.2 единицы ловит 'почти-контакт' — куб
собирается при подходе на ~1 единицу.
2026-06-09 18:12:01 +03:00
min
8295d6f0fe fix(g7): синк позиций спавненных частей в shim для AABB-touched-check
DynamicsManager._applyToMesh обновляет pm.instances[id].x/y/z, но Lua-shim
кэширует Position в part._state.Position на момент создания. AABB-check
видел кубы навечно в небе → касание не ловилось.

Фикс: GameRuntime.tick собирает позиции всех спавненных динамических
примитивов и шлёт в shim через api.updateSpawnedPos(id, x, y, z).
Shim обновляет part._state.Position у соответствующего partById.
2026-06-09 18:09:45 +03:00
min
462ee62a9a fix(g7): Touched на спавненных частях через AABB-check в Heartbeat
BabylonScene._detectTouchEvents работает только для скриптов с явным
target. Спавненные runtime через __rbxl_spawn_part (падающие кубы)
не имеют target — Babylon их не проверяет, Touched молчит.

Решение: shim.fireHeartbeat теперь сам делает AABB игрок↔part для
всех part id >= 800000 (наш range спавненных). При пересечении
фейерит Touched.Fire(hrp); при выходе — TouchEnded.
2026-06-09 18:07:25 +03:00
min
fe2c1bb28b fix(g7): scene3d.dynamics (не dynamicsManager) — теперь регистрация работает 2026-06-09 18:04:14 +03:00
min
2c99a61bb0 debug(g7): лог про регистрацию unanchored part в физике 2026-06-09 18:02:47 +03:00