From 26e6306f6e8e94eba2a242f719b1556191d6c0bf Mon Sep 17 00:00:00 2001 From: min Date: Fri, 5 Jun 2026 19:54:38 +0300 Subject: [PATCH] =?UTF-8?q?fix(studio):=20=D0=B4=D0=B2=D0=B5=D1=80=D1=8C-?= =?UTF-8?q?=D0=BA=D0=BE=D0=B4=20=D0=BF=D0=BE=D0=B4=D1=81=D0=BA=D0=B0=D0=B7?= =?UTF-8?q?=D0=BA=D0=B0=20E=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D0=BE?= =?UTF-8?q?=D0=B9,=20NPC=3DR15-=D1=81=D0=BA=D0=B8=D0=BD=20(=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=20=D1=80=D1=83=D0=BA/?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3),=20=D1=82=D0=BE=D1=80=D0=B3=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=86=20=D0=B2=D0=B8=D0=B4=D0=B8=D0=BC=D1=8B=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) Дверь по коду: подсказка «E закрыть» теперь только когда дверь открыта и игрок рядом (через onKey+onTick, без постоянного interact-промпта). 2) NPC-киты используют R15-скин 'skin_roblox-noob' → процедурная анимация бега/покоя с руками и ногами (R15Animator), вместо безжизненного покачивания. 3) Торговец-кит: невидимый триггер (insertGameplayKit теперь уважает prim.visible=false, раньше хардкод visible:true) + NPC-персонаж рядом — синий куб больше не виден. Co-Authored-By: Claude Opus 4.8 --- src/editor/KubikonEditor.jsx | 2 +- src/editor/engine/GameplayKits.js | 35 ++++++++++++++++++------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/editor/KubikonEditor.jsx b/src/editor/KubikonEditor.jsx index bbf3c6a..3717b0b 100644 --- a/src/editor/KubikonEditor.jsx +++ b/src/editor/KubikonEditor.jsx @@ -800,7 +800,7 @@ const KubikonEditor = () => { x: px + (p.x || 0), y: py + (p.y != null ? p.y : 1), z: pz + (p.z || 0), sx: p.sx, sy: p.sy, sz: p.sz, color: p.color, material: p.material, - canCollide: p.canCollide !== false, visible: true, anchored: true, + canCollide: p.canCollide !== false, visible: p.visible !== false, anchored: true, name: p.name, }); if (newId != null) { diff --git a/src/editor/engine/GameplayKits.js b/src/editor/engine/GameplayKits.js index f35dd8a..7d38b59 100644 --- a/src/editor/engine/GameplayKits.js +++ b/src/editor/engine/GameplayKits.js @@ -617,10 +617,15 @@ function place(a){ } game.onTick((dt) => { if (cur !== target){ const st=SPEED*dt; cur = Math.abs(target-cur)<=st ? target : cur+Math.sign(target-cur)*st; place(cur); } - if (opened) return; - // Поле ввода появляется/прячется по дистанции. const pl = game.player.position; const d = Math.sqrt((pl.x-p0.x)**2 + (pl.z-p0.z)**2); + if (opened){ + // Дверь открыта: подсказка «E закрыть» только когда игрок рядом. + if (d < RADIUS && !near){ near = true; game.ui.set('codehint','Нажми E чтобы закрыть дверь', {x:50,y:80,anchor:'bottom',color:'#fff',size:16}); } + else if (d >= RADIUS && near){ near = false; game.ui.set('codehint','',{}); } + return; + } + // Дверь закрыта: поле ввода кода по дистанции. if (d < RADIUS && !near){ near = true; game.gui.create('textbox', { id:'codein', x:50, y:86, w:24, h:8, anchor:'center', placeholder:'Код...', textSize:20 }); game.ui.set('codehint', '🔢 Введи код двери (1234) и нажми Enter', {x:50,y:78,anchor:'bottom',color:'#fff',size:16}); } @@ -629,19 +634,21 @@ game.onTick((dt) => { game.gui.onSubmit('codein', (text) => { if (opened) return; if (String(text).trim() === CODE){ - opened = true; target = Math.PI/2; // плавно распахнуть + opened = true; near = false; target = Math.PI/2; // плавно распахнуть game.gui.remove('codein'); - game.ui.set('codehint','✓ Открыто! Нажми E чтобы закрыть.', {x:50,y:78,anchor:'bottom',color:'#36d57a',size:16}); - game.after(2.5, () => game.ui.set('codehint','',{})); + game.ui.set('codehint','✓ Открыто!', {x:50,y:78,anchor:'bottom',color:'#36d57a',size:18}); + game.after(2, () => game.ui.set('codehint','',{})); } else game.ui.set('codehint','✗ Неверный код', {x:50,y:78,anchor:'bottom',color:'#ff5555',size:18}); }); -// Закрыть открытую дверь по E → снова можно вводить код. -game.self.onInteract(() => { +// Закрыть дверь по E (только если открыта и игрок рядом). +game.onKey('e', () => { if (!opened) return; - opened = false; near = false; target = 0; // плавно закрыть - game.ui.set('codehint','🔒 Дверь закрыта.', {x:50,y:78,anchor:'bottom',color:'#fff',size:16}); - game.after(2, () => game.ui.set('codehint','',{})); -}, { text: 'Закрыть дверь', key: 'e', distance: 4 });` }], + const pl = game.player.position; + if (Math.sqrt((pl.x-p0.x)**2 + (pl.z-p0.z)**2) >= RADIUS) return; + opened = false; near = false; target = 0; + game.ui.set('codehint','🔒 Дверь закрыта.', {x:50,y:80,anchor:'bottom',color:'#fff',size:16}); + game.after(1.5, () => game.ui.set('codehint','',{})); +});` }], }, { id: 'name-label', @@ -708,7 +715,7 @@ game.onTick(() => { icon: 'chase', category: 'npc', scripts: [{ attachTo: 'global', code: `// Спавним NPC, который преследует игрока. -const enemy = game.scene.spawnNpc('character-a', { x: 8, z: 8, name: 'Охотник', speed: 4 }); +const enemy = game.scene.spawnNpc('skin_roblox-noob', { x: 8, z: 8, name: 'Охотник', speed: 4 }); if (enemy && enemy.follow) enemy.follow('player');` }], }, { @@ -721,7 +728,7 @@ if (enemy && enemy.follow) enemy.follow('player');` }], scripts: [{ attachTo: 'on-target', code: `// Торговец — настоящий NPC-персонаж. Триггер (этот объект) держит диалог по E. const p = game.self.position; -const npc = game.scene.spawnNpc('character-a', { x: p.x, z: p.z, name: 'Торговец Боб' }); +const npc = game.scene.spawnNpc('skin_roblox-noob', { x: p.x, z: p.z, name: 'Торговец Боб' }); game.self.onInteract(() => { game.modal.dialog('Торговец Боб', [ 'Привет, путник! Заходи за товарами.', @@ -777,7 +784,7 @@ game.self.onClick(() => { `// Каждые 5с спавнит 2 врагов из точки портала, они идут к игроку. const p = game.self.position; function wave(){ - for (let i=0;i<2;i++){ const e = game.scene.spawnNpc('character-a', { x:p.x+(Math.random()-0.5)*2, z:p.z+(Math.random()-0.5)*2, name:'Враг', speed:3 }); + for (let i=0;i<2;i++){ const e = game.scene.spawnNpc('skin_roblox-noob', { x:p.x+(Math.random()-0.5)*2, z:p.z+(Math.random()-0.5)*2, name:'Враг', speed:3 }); if (e && e.follow) e.follow('player'); } } game.after(2, wave); game.every(5, wave);` }],