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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
5186ee3b70
feat(g29): добавлен NPC-продавец за прилавком с меткой 'Продавец'
2026-06-09 22:03:06 +03:00
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
ed23ec782c
docs: CodeBoth для урока 28 «Призрачные стены» (main+wall+finish)
2026-06-09 21:58:44 +03:00
a72101a29a
fix(g28): ClickDetector вместо part.Clicked (как в Тире)
...
part.Clicked не существует — был fallback try/catch с молчаливым
падением. ClickDetector + MouseClick — рабочий путь (используется
в игре 15 «Тир»).
2026-06-09 21:57:38 +03:00
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
b9473cca50
docs: CodeBoth для урока 27 «Двойной прыжок» (main+finish)
2026-06-09 21:51:37 +03:00
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
19c475f132
docs: CodeBoth для урока 26 «Магнит монет» (main+coin)
2026-06-09 21:48:31 +03:00
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
bc1214e600
docs: CodeBoth для урока 25 «Камера-облёт» (main+finish)
2026-06-09 21:45:14 +03:00
189a23ff7c
fix(g25): передаём lookAt-точки чтобы камера вращалась к финишу
...
cameraCutscene без lookAt держит угол постоянным (setTarget не зовётся).
Добавил 3-й arg в __rbxl_camera_cutscene и 4 lookAt-точки указывающие
вдоль пути — камера плавно поворачивается к финишу.
2026-06-09 21:44:14 +03:00
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
2a39fc2b99
docs: CodeBoth для урока 24 «Падающий мост» (main+plank+finish)
2026-06-09 21:38:24 +03:00
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
745e50703d
docs: CodeBoth для урока 23 «Переключатели» (main+lever+finish)
2026-06-09 21:34:59 +03:00
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
791cf2cde5
docs: CodeBoth для урока 22 «Зона опасности» (main+zone+heal+finish)
2026-06-09 21:31:22 +03:00
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
cbc4b87643
docs: CodeBoth для урока 21 «Преследователь» (main+finish)
2026-06-09 21:25:52 +03:00
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
d7478fe311
docs: CodeBoth для урока 20 «Имена над врагами» (g20_main)
2026-06-09 21:20:38 +03:00
58fbc9d6e6
fix(g20): метка height 2.5 — над health-bar
2026-06-09 21:18:50 +03:00
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
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
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
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
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
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
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
65de311d59
debug(g20): лог npc.spawn/damage/setLabel в LuaSharedSandbox onCommand
2026-06-09 20:53:01 +03:00
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
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
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
3cdbbc5049
docs: CodeBoth для урока 19 «Лифт» (main+finish)
2026-06-09 20:28:37 +03:00